diff --git a/README.md b/README.md index 2b334805f2..a6f3f0745a 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ is more portable across Linux distributions. | `aarch64-linux-gnu` | OK | | `aarch64-linux-musl` | OK | | `aarch64-windows-gnu` | not tested | -| `aarch64-macos-gnu` | [#44](https://github.com/ziglang/zig-bootstrap/issues/44) | +| `aarch64-macos-gnu` | OK | | `armeb-linux-gnueabi` | not tested | | `armeb-linux-gnueabihf` | not tested | | `armeb-linux-musleabi` | not tested | diff --git a/build b/build index dfc5573128..f933d6383d 100755 --- a/build +++ b/build @@ -7,7 +7,7 @@ TARGET="$2" # Example: riscv64-linux-gnu MCPU="$3" # Examples: `baseline`, `native`, `generic+v7a`, or `arm1176jzf_s` ROOTDIR="$(pwd)" -ZIG_VERSION="0.8.0-dev.1939+5a3ea9bec" +ZIG_VERSION="0.8.0-dev.1813+88d40fc00" TARGET_OS_AND_ABI=${TARGET#*-} # Example: linux-gnu diff --git a/zig/.gitattributes b/zig/.gitattributes index 8bf5843425..6cf47bc9ad 100644 --- a/zig/.gitattributes +++ b/zig/.gitattributes @@ -3,9 +3,9 @@ langref.html.in text eol=lf deps/SoftFloat-3e/*.txt text eol=crlf -deps/* linguist-vendored -lib/include/* linguist-vendored -lib/libc/* linguist-vendored -lib/libcxx/* linguist-vendored -lib/libcxxabi/* linguist-vendored -lib/libunwind/* linguist-vendored +deps/** linguist-vendored +lib/include/** linguist-vendored +lib/libc/** linguist-vendored +lib/libcxx/** linguist-vendored +lib/libcxxabi/** linguist-vendored +lib/libunwind/** linguist-vendored diff --git a/zig/.github/FUNDING.yml b/zig/.github/FUNDING.yml new file mode 100644 index 0000000000..02ba7f0d31 --- /dev/null +++ b/zig/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [ziglang] diff --git a/zig/build.zig b/zig/build.zig index 92e03603c5..a0bc6a89c3 100644 --- a/zig/build.zig +++ b/zig/build.zig @@ -36,7 +36,7 @@ pub fn build(b: *Builder) !void { const docs_step = b.step("docs", "Build documentation"); docs_step.dependOn(&docgen_cmd.step); - const test_step = b.step("test", "Run all the tests"); + const toolchain_step = b.step("test-toolchain", "Run the tests for the toolchain"); var test_stage2 = b.addTest("src/test.zig"); test_stage2.setBuildMode(mode); @@ -44,6 +44,7 @@ pub fn build(b: *Builder) !void { const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"}); + const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false; const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false; const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release; const skip_release_fast = b.option(bool, "skip-release-fast", "Main test suite skips release-fast builds") orelse skip_release; @@ -51,9 +52,12 @@ pub fn build(b: *Builder) !void { const skip_non_native = b.option(bool, "skip-non-native", "Main test suite skips non-native builds") orelse false; const skip_libc = b.option(bool, "skip-libc", "Main test suite skips tests that link libc") orelse false; const skip_compile_errors = b.option(bool, "skip-compile-errors", "Main test suite skips compile error tests") orelse false; + const skip_run_translated_c = b.option(bool, "skip-run-translated-c", "Main test suite skips run-translated-c tests") orelse false; + const skip_stage2_tests = b.option(bool, "skip-stage2-tests", "Main test suite skips self-hosted compiler tests") orelse false; const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false; const is_stage1 = b.option(bool, "stage1", "Build the stage1 compiler, put stage2 behind a feature flag") orelse false; + const omit_stage2 = b.option(bool, "omit-stage2", "Do not include stage2 behind a feature flag inside stage1") orelse false; const static_llvm = b.option(bool, "static-llvm", "Disable integration with system-installed LLVM, Clang, LLD, and libc++") orelse false; const enable_llvm = b.option(bool, "enable-llvm", "Build self-hosted compiler with LLVM backend enabled") orelse (is_stage1 or static_llvm); const config_h_path_option = b.option([]const u8, "config_h", "Path to the generated config.h"); @@ -86,7 +90,7 @@ pub fn build(b: *Builder) !void { exe.install(); exe.setBuildMode(mode); exe.setTarget(target); - test_step.dependOn(&exe.step); + toolchain_step.dependOn(&exe.step); b.default_step.dependOn(&exe.step); exe.addBuildOption(bool, "skip_non_native", skip_non_native); @@ -195,7 +199,7 @@ pub fn build(b: *Builder) !void { exe.addBuildOption(bool, "enable_logging", enable_logging); exe.addBuildOption(bool, "enable_tracy", tracy != null); exe.addBuildOption(bool, "is_stage1", is_stage1); - exe.addBuildOption(bool, "omit_stage2", false); + exe.addBuildOption(bool, "omit_stage2", omit_stage2); if (tracy) |tracy_path| { const client_cpp = fs.path.join( b.allocator, @@ -218,7 +222,7 @@ pub fn build(b: *Builder) !void { test_stage2.addBuildOption(bool, "skip_non_native", skip_non_native); test_stage2.addBuildOption(bool, "is_stage1", is_stage1); - test_stage2.addBuildOption(bool, "omit_stage2", false); + test_stage2.addBuildOption(bool, "omit_stage2", omit_stage2); test_stage2.addBuildOption(bool, "have_llvm", enable_llvm); test_stage2.addBuildOption(bool, "enable_qemu", is_qemu_enabled); test_stage2.addBuildOption(bool, "enable_wine", is_wine_enabled); @@ -228,12 +232,16 @@ pub fn build(b: *Builder) !void { const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests"); test_stage2_step.dependOn(&test_stage2.step); - test_step.dependOn(test_stage2_step); + if (!skip_stage2_tests) { + toolchain_step.dependOn(test_stage2_step); + } var chosen_modes: [4]builtin.Mode = undefined; var chosen_mode_index: usize = 0; - chosen_modes[chosen_mode_index] = builtin.Mode.Debug; - chosen_mode_index += 1; + if (!skip_debug) { + chosen_modes[chosen_mode_index] = builtin.Mode.Debug; + chosen_mode_index += 1; + } if (!skip_release_safe) { chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSafe; chosen_mode_index += 1; @@ -249,30 +257,37 @@ pub fn build(b: *Builder) !void { const modes = chosen_modes[0..chosen_mode_index]; // run stage1 `zig fmt` on this build.zig file just to make sure it works - test_step.dependOn(&fmt_build_zig.step); + toolchain_step.dependOn(&fmt_build_zig.step); const fmt_step = b.step("test-fmt", "Run zig fmt against build.zig to make sure it works"); fmt_step.dependOn(&fmt_build_zig.step); // TODO for the moment, skip wasm32-wasi until bugs are sorted out. - test_step.dependOn(tests.addPkgTests(b, test_filter, "test/stage1/behavior.zig", "behavior", "Run the behavior tests", modes, false, skip_non_native, skip_libc, is_wine_enabled, is_qemu_enabled, is_wasmtime_enabled, glibc_multi_dir)); - - test_step.dependOn(tests.addPkgTests(b, test_filter, "lib/std/std.zig", "std", "Run the standard library tests", modes, false, skip_non_native, skip_libc, is_wine_enabled, is_qemu_enabled, is_wasmtime_enabled, glibc_multi_dir)); - - test_step.dependOn(tests.addPkgTests(b, test_filter, "lib/std/special/compiler_rt.zig", "compiler-rt", "Run the compiler_rt tests", modes, true, skip_non_native, true, is_wine_enabled, is_qemu_enabled, is_wasmtime_enabled, glibc_multi_dir)); - - test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); - test_step.dependOn(tests.addStandaloneTests(b, test_filter, modes)); - test_step.dependOn(tests.addStackTraceTests(b, test_filter, modes)); - test_step.dependOn(tests.addCliTests(b, test_filter, modes)); - test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes)); - test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes)); - test_step.dependOn(tests.addTranslateCTests(b, test_filter)); - test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target)); + toolchain_step.dependOn(tests.addPkgTests(b, test_filter, "test/stage1/behavior.zig", "behavior", "Run the behavior tests", modes, false, skip_non_native, skip_libc, is_wine_enabled, is_qemu_enabled, is_wasmtime_enabled, glibc_multi_dir)); + + toolchain_step.dependOn(tests.addPkgTests(b, test_filter, "lib/std/special/compiler_rt.zig", "compiler-rt", "Run the compiler_rt tests", modes, true, skip_non_native, true, is_wine_enabled, is_qemu_enabled, is_wasmtime_enabled, glibc_multi_dir)); + toolchain_step.dependOn(tests.addPkgTests(b, test_filter, "lib/std/special/c.zig", "minilibc", "Run the mini libc tests", modes, true, skip_non_native, true, is_wine_enabled, is_qemu_enabled, is_wasmtime_enabled, glibc_multi_dir)); + + toolchain_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); + toolchain_step.dependOn(tests.addStandaloneTests(b, test_filter, modes)); + toolchain_step.dependOn(tests.addStackTraceTests(b, test_filter, modes)); + toolchain_step.dependOn(tests.addCliTests(b, test_filter, modes)); + toolchain_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes)); + toolchain_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes)); + toolchain_step.dependOn(tests.addTranslateCTests(b, test_filter)); + if (!skip_run_translated_c) { + toolchain_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target)); + } // tests for this feature are disabled until we have the self-hosted compiler available - // test_step.dependOn(tests.addGenHTests(b, test_filter)); + // toolchain_step.dependOn(tests.addGenHTests(b, test_filter)); if (!skip_compile_errors) { - test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes)); + toolchain_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes)); } + + const std_step = tests.addPkgTests(b, test_filter, "lib/std/std.zig", "std", "Run the standard library tests", modes, false, skip_non_native, skip_libc, is_wine_enabled, is_qemu_enabled, is_wasmtime_enabled, glibc_multi_dir); + + const test_step = b.step("test", "Run all the tests"); + test_step.dependOn(toolchain_step); + test_step.dependOn(std_step); test_step.dependOn(docs_step); } @@ -739,80 +754,60 @@ const lld_libs = [_][]const u8{ }; // This list can be re-generated with `llvm-config --libfiles` and then // reformatting using your favorite text editor. Note we do not execute -// `llvm-config` here because we are cross compiling. +// `llvm-config` here because we are cross compiling. Also omit LLVMTableGen +// from these libs. const llvm_libs = [_][]const u8{ - "LLVMXRay", "LLVMWindowsManifest", - "LLVMSymbolize", - "LLVMDebugInfoPDB", - "LLVMOrcJIT", - "LLVMOrcError", - "LLVMJITLink", - "LLVMObjectYAML", - "LLVMMCA", - "LLVMLTO", - "LLVMPasses", - "LLVMCoroutines", - "LLVMObjCARCOpts", - "LLVMExtensions", - "LLVMLineEditor", + "LLVMXRay", "LLVMLibDriver", - "LLVMInterpreter", - "LLVMFuzzMutate", - "LLVMMCJIT", - "LLVMExecutionEngine", - "LLVMRuntimeDyld", - "LLVMDWARFLinker", "LLVMDlltoolDriver", - "LLVMOption", - "LLVMDebugInfoGSYM", "LLVMCoverage", + "LLVMLineEditor", "LLVMXCoreDisassembler", "LLVMXCoreCodeGen", "LLVMXCoreDesc", "LLVMXCoreInfo", "LLVMX86Disassembler", - "LLVMX86CodeGen", "LLVMX86AsmParser", + "LLVMX86CodeGen", "LLVMX86Desc", "LLVMX86Info", "LLVMWebAssemblyDisassembler", + "LLVMWebAssemblyAsmParser", "LLVMWebAssemblyCodeGen", "LLVMWebAssemblyDesc", - "LLVMWebAssemblyAsmParser", "LLVMWebAssemblyInfo", "LLVMSystemZDisassembler", - "LLVMSystemZCodeGen", "LLVMSystemZAsmParser", + "LLVMSystemZCodeGen", "LLVMSystemZDesc", "LLVMSystemZInfo", "LLVMSparcDisassembler", - "LLVMSparcCodeGen", "LLVMSparcAsmParser", + "LLVMSparcCodeGen", "LLVMSparcDesc", "LLVMSparcInfo", "LLVMRISCVDisassembler", - "LLVMRISCVCodeGen", "LLVMRISCVAsmParser", + "LLVMRISCVCodeGen", "LLVMRISCVDesc", - "LLVMRISCVUtils", "LLVMRISCVInfo", "LLVMPowerPCDisassembler", - "LLVMPowerPCCodeGen", "LLVMPowerPCAsmParser", + "LLVMPowerPCCodeGen", "LLVMPowerPCDesc", "LLVMPowerPCInfo", "LLVMNVPTXCodeGen", "LLVMNVPTXDesc", "LLVMNVPTXInfo", "LLVMMSP430Disassembler", - "LLVMMSP430CodeGen", "LLVMMSP430AsmParser", + "LLVMMSP430CodeGen", "LLVMMSP430Desc", "LLVMMSP430Info", "LLVMMipsDisassembler", - "LLVMMipsCodeGen", "LLVMMipsAsmParser", + "LLVMMipsCodeGen", "LLVMMipsDesc", "LLVMMipsInfo", "LLVMLanaiDisassembler", @@ -826,44 +821,73 @@ const llvm_libs = [_][]const u8{ "LLVMHexagonDesc", "LLVMHexagonInfo", "LLVMBPFDisassembler", - "LLVMBPFCodeGen", "LLVMBPFAsmParser", + "LLVMBPFCodeGen", "LLVMBPFDesc", "LLVMBPFInfo", "LLVMAVRDisassembler", - "LLVMAVRCodeGen", "LLVMAVRAsmParser", + "LLVMAVRCodeGen", "LLVMAVRDesc", "LLVMAVRInfo", "LLVMARMDisassembler", - "LLVMARMCodeGen", "LLVMARMAsmParser", + "LLVMARMCodeGen", "LLVMARMDesc", "LLVMARMUtils", "LLVMARMInfo", "LLVMAMDGPUDisassembler", - "LLVMAMDGPUCodeGen", - "LLVMMIRParser", - "LLVMipo", - "LLVMInstrumentation", - "LLVMVectorize", - "LLVMLinker", - "LLVMIRReader", - "LLVMAsmParser", - "LLVMFrontendOpenMP", "LLVMAMDGPUAsmParser", + "LLVMAMDGPUCodeGen", "LLVMAMDGPUDesc", "LLVMAMDGPUUtils", "LLVMAMDGPUInfo", "LLVMAArch64Disassembler", - "LLVMMCDisassembler", + "LLVMAArch64AsmParser", "LLVMAArch64CodeGen", + "LLVMAArch64Desc", + "LLVMAArch64Utils", + "LLVMAArch64Info", + "LLVMOrcJIT", + "LLVMMCJIT", + "LLVMJITLink", + "LLVMOrcTargetProcess", + "LLVMOrcShared", + "LLVMInterpreter", + "LLVMExecutionEngine", + "LLVMRuntimeDyld", + "LLVMSymbolize", + "LLVMDebugInfoPDB", + "LLVMDebugInfoGSYM", + "LLVMOption", + "LLVMObjectYAML", + "LLVMMCA", + "LLVMMCDisassembler", + "LLVMLTO", + "LLVMPasses", "LLVMCFGuard", + "LLVMCoroutines", + "LLVMObjCARCOpts", + "LLVMHelloNew", + "LLVMipo", + "LLVMVectorize", + "LLVMLinker", + "LLVMInstrumentation", + "LLVMFrontendOpenMP", + "LLVMFrontendOpenACC", + "LLVMExtensions", + "LLVMDWARFLinker", "LLVMGlobalISel", - "LLVMSelectionDAG", + "LLVMMIRParser", "LLVMAsmPrinter", "LLVMDebugInfoDWARF", + "LLVMSelectionDAG", "LLVMCodeGen", + "LLVMIRReader", + "LLVMAsmParser", + "LLVMInterfaceStub", + "LLVMFileCheck", + "LLVMFuzzMutate", "LLVMTarget", "LLVMScalarOpts", "LLVMInstCombine", @@ -874,19 +898,15 @@ const llvm_libs = [_][]const u8{ "LLVMProfileData", "LLVMObject", "LLVMTextAPI", - "LLVMBitReader", - "LLVMCore", - "LLVMRemarks", - "LLVMBitstreamReader", - "LLVMAArch64AsmParser", "LLVMMCParser", - "LLVMAArch64Desc", "LLVMMC", "LLVMDebugInfoCodeView", "LLVMDebugInfoMSF", + "LLVMBitReader", + "LLVMCore", + "LLVMRemarks", + "LLVMBitstreamReader", "LLVMBinaryFormat", - "LLVMAArch64Utils", - "LLVMAArch64Info", "LLVMSupport", "LLVMDemangle", }; diff --git a/zig/ci/azure/linux_script b/zig/ci/azure/linux_script index e96a16b41b..912a2518bb 100755 --- a/zig/ci/azure/linux_script +++ b/zig/ci/azure/linux_script @@ -9,7 +9,7 @@ sudo apt-get install -y cmake s3cmd tidy ZIGDIR="$(pwd)" ARCH="$(uname -m)" TARGET="$ARCH-linux-musl" -CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.8.0-dev.859+f1ef0a80f" +CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.8.0-dev.1939+5a3ea9bec" PREFIX="$HOME/$CACHE_BASENAME" MCPU="baseline" JOBS="-j$(nproc)" @@ -65,7 +65,9 @@ make $JOBS install cmake .. -DZIG_EXECUTABLE="$(pwd)/release/bin/zig" make $JOBS install -release/bin/zig build test -Denable-qemu -Denable-wasmtime +for step in test-toolchain test-std docs; do + release/bin/zig build $step -Denable-qemu -Denable-wasmtime +done # Look for HTML errors. tidy -qe ../zig-cache/langref.html diff --git a/zig/ci/azure/macos_arm64_script b/zig/ci/azure/macos_arm64_script index e7aa45b281..54d0380fa4 100755 --- a/zig/ci/azure/macos_arm64_script +++ b/zig/ci/azure/macos_arm64_script @@ -8,8 +8,8 @@ brew update && brew install s3cmd ninja gnu-tar ZIGDIR="$(pwd)" ARCH="aarch64" # {product}-{os}{sdk_version}-{arch}-{llvm_version}-{cmake_build_type} -CACHE_HOST_BASENAME="llvm-macos10.15-x86_64-11.0.1-release" -CACHE_ARM64_BASENAME="llvm-macos11.0-arm64-11.0.1-release" +CACHE_HOST_BASENAME="ci-llvm-macos10.15-x86_64-12.0.0.1-release" +CACHE_ARM64_BASENAME="ci-llvm-macos11.0-arm64-12.0.0.1-release" PREFIX_HOST="$HOME/$CACHE_HOST_BASENAME" PREFIX_ARM64="$HOME/$CACHE_ARM64_BASENAME" JOBS="-j2" diff --git a/zig/ci/azure/macos_script b/zig/ci/azure/macos_script index d47575acc0..d6d32612cc 100755 --- a/zig/ci/azure/macos_script +++ b/zig/ci/azure/macos_script @@ -7,7 +7,7 @@ brew update && brew install s3cmd ZIGDIR="$(pwd)" ARCH="x86_64" -CACHE_BASENAME="zig+llvm+lld+clang-$ARCH-macos-gnu-0.6.0+1c9ef63a" +CACHE_BASENAME="zig+llvm+lld+clang-$ARCH-macos-gnu-0.8.0-dev.1939+5a3ea9bec" PREFIX="$HOME/$CACHE_BASENAME" JOBS="-j2" @@ -18,7 +18,7 @@ tar xf "$CACHE_BASENAME.tar.xz" ZIG="$PREFIX/bin/zig" NATIVE_LIBC_TXT="$HOME/native_libc.txt" -$ZIG libc > "$NATIVE_LIBC_TXT" +$ZIG libc >"$NATIVE_LIBC_TXT" export ZIG_LIBC="$NATIVE_LIBC_TXT" export CC="$ZIG cc" export CXX="$ZIG c++" @@ -52,10 +52,12 @@ make $JOBS install # Here we rebuild zig but this time using the Zig binary we just now produced to # build zig1.o rather than relying on the one built with stage0. See # https://github.com/ziglang/zig/issues/6830 for more details. -cmake .. -DZIG_EXECUTABLE="$(pwd)/release/bin/zig" +cmake .. -DZIG_EXECUTABLE="$(pwd)/release/bin/zig" -DZIG_TARGET_MCPU="x86_64_v2" make $JOBS install -release/bin/zig build test +for step in test-toolchain test-std docs; do + release/bin/zig build $step +done if [ "${BUILD_REASON}" != "PullRequest" ]; then mv ../LICENSE release/ diff --git a/zig/ci/azure/windows_msvc_install b/zig/ci/azure/windows_msvc_install index 7df1aa6461..bdc4bd0bf1 100644 --- a/zig/ci/azure/windows_msvc_install +++ b/zig/ci/azure/windows_msvc_install @@ -7,5 +7,5 @@ pacman -Suy --needed --noconfirm pacman -S --needed --noconfirm wget p7zip python3-pip tar xz pip install s3cmd -wget -nv "https://ziglang.org/deps/llvm%2bclang%2blld-11.0.0-x86_64-windows-msvc-release-mt.tar.xz" -tar xf llvm+clang+lld-11.0.0-x86_64-windows-msvc-release-mt.tar.xz +wget -nv "https://ziglang.org/deps/llvm%2bclang%2blld-12.0.0-x86_64-windows-msvc-release-mt.tar.xz" +tar xf llvm+clang+lld-12.0.0-x86_64-windows-msvc-release-mt.tar.xz diff --git a/zig/ci/azure/windows_msvc_script.bat b/zig/ci/azure/windows_msvc_script.bat index 9d28eccd0b..fc17c80ea2 100644 --- a/zig/ci/azure/windows_msvc_script.bat +++ b/zig/ci/azure/windows_msvc_script.bat @@ -11,7 +11,7 @@ SET "MSYSTEM=%PREVMSYSTEM%" SET "ZIGBUILDDIR=%SRCROOT%\build" SET "ZIGINSTALLDIR=%ZIGBUILDDIR%\dist" -SET "ZIGPREFIXPATH=%SRCROOT%\llvm+clang+lld-11.0.0-x86_64-windows-msvc-release-mt" +SET "ZIGPREFIXPATH=%SRCROOT%\llvm+clang+lld-12.0.0-x86_64-windows-msvc-release-mt" call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 @@ -26,20 +26,8 @@ cd %ZIGBUILDDIR% cmake.exe .. -Thost=x64 -G"Visual Studio 16 2019" -A x64 "-DCMAKE_INSTALL_PREFIX=%ZIGINSTALLDIR%" "-DCMAKE_PREFIX_PATH=%ZIGPREFIXPATH%" -DCMAKE_BUILD_TYPE=Release -DZIG_OMIT_STAGE2=ON || exit /b msbuild /maxcpucount /p:Configuration=Release INSTALL.vcxproj || exit /b -"%ZIGINSTALLDIR%\bin\zig.exe" build test-behavior -Dskip-non-native || exit /b -REM Disabled to prevent OOM -REM "%ZIGINSTALLDIR%\bin\zig.exe" build test-stage2 -Dskip-non-native || exit /b -"%ZIGINSTALLDIR%\bin\zig.exe" build test-fmt -Dskip-non-native || exit /b +"%ZIGINSTALLDIR%\bin\zig.exe" build test-toolchain -Dskip-non-native -Dskip-stage2-tests || exit /b "%ZIGINSTALLDIR%\bin\zig.exe" build test-std -Dskip-non-native || exit /b -"%ZIGINSTALLDIR%\bin\zig.exe" build test-compiler-rt -Dskip-non-native || exit /b -"%ZIGINSTALLDIR%\bin\zig.exe" build test-compare-output -Dskip-non-native || exit /b -"%ZIGINSTALLDIR%\bin\zig.exe" build test-standalone -Dskip-non-native || exit /b -"%ZIGINSTALLDIR%\bin\zig.exe" build test-stack-traces -Dskip-non-native || exit /b -"%ZIGINSTALLDIR%\bin\zig.exe" build test-cli -Dskip-non-native || exit /b -"%ZIGINSTALLDIR%\bin\zig.exe" build test-asm-link -Dskip-non-native || exit /b -"%ZIGINSTALLDIR%\bin\zig.exe" build test-runtime-safety -Dskip-non-native || exit /b -"%ZIGINSTALLDIR%\bin\zig.exe" build test-translate-c -Dskip-non-native || exit /b -"%ZIGINSTALLDIR%\bin\zig.exe" build test-run-translated-c -Dskip-non-native || exit /b "%ZIGINSTALLDIR%\bin\zig.exe" build docs || exit /b set "PATH=%CD:~0,2%\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem" diff --git a/zig/ci/drone/drone.yml b/zig/ci/drone/drone.yml index 02df735b6b..f0c4ab66d4 100644 --- a/zig/ci/drone/drone.yml +++ b/zig/ci/drone/drone.yml @@ -6,8 +6,39 @@ platform: arch: arm64 steps: -- name: build-and-test - image: ziglang/static-base:llvm11-aarch64-1 +- name: build + image: ziglang/static-base:llvm12-aarch64-1 + commands: + - ./ci/drone/linux_script_build + +- name: test-1 + depends_on: + - build + image: ziglang/static-base:llvm12-aarch64-1 + commands: + - ./ci/drone/linux_script_test 1 + +- name: test-2 + depends_on: + - build + image: ziglang/static-base:llvm12-aarch64-1 + commands: + - ./ci/drone/linux_script_test 2 + +- name: test-3 + depends_on: + - build + image: ziglang/static-base:llvm12-aarch64-1 + commands: + - ./ci/drone/linux_script_test 3 + +- name: finalize + depends_on: + - build + - test-1 + - test-2 + - test-3 + image: ziglang/static-base:llvm12-aarch64-1 environment: SRHT_OAUTH_TOKEN: from_secret: SRHT_OAUTH_TOKEN @@ -16,4 +47,4 @@ steps: AWS_SECRET_ACCESS_KEY: from_secret: AWS_SECRET_ACCESS_KEY commands: - - ./ci/drone/linux_script + - ./ci/drone/linux_script_finalize diff --git a/zig/ci/drone/linux_script b/zig/ci/drone/linux_script deleted file mode 100755 index dbe13f6f19..0000000000 --- a/zig/ci/drone/linux_script +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/sh - -set -x -set -e - -TRIPLEARCH="$(uname -m)" -BUILDDIR="$(pwd)" -DISTDIR="$(pwd)/dist" - -apk update -apk add py3-pip xz perl-utils jq curl samurai -pip3 install s3cmd - -# Make the `zig version` number consistent. -# This will affect the cmake command below. -git config core.abbrev 9 -git fetch --unshallow || true -git fetch --tags - -mkdir build -cd build -cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_INSTALL_PREFIX=$DISTDIR" -DZIG_STATIC=ON -DCMAKE_PREFIX_PATH=/deps/local -GNinja - -samu install -./zig build test -Dskip-release -Dskip-non-native -Dskip-compile-errors - -if [ -z "$DRONE_PULL_REQUEST" ]; then - mv ../LICENSE "$DISTDIR/" - mv ../zig-cache/langref.html "$DISTDIR/" - mv "$DISTDIR/bin/zig" "$DISTDIR/" - rmdir "$DISTDIR/bin" - - GITBRANCH="$DRONE_BRANCH" - VERSION="$("$DISTDIR/zig" version)" - DIRNAME="zig-linux-$TRIPLEARCH-$VERSION" - TARBALL="$DIRNAME.tar.xz" - mv "$DISTDIR" "$DIRNAME" - tar cfJ "$TARBALL" "$DIRNAME" - - s3cmd put -P --add-header="cache-control: public, max-age=31536000, immutable" "$TARBALL" s3://ziglang.org/builds/ - - SHASUM=$(shasum -a 256 $TARBALL | cut '-d ' -f1) - BYTESIZE=$(wc -c < $TARBALL) - - JSONFILE="$TRIPLEARCH-linux-$GITBRANCH.json" - touch $JSONFILE - echo "{\"tarball\": \"$TARBALL\"," >>$JSONFILE - echo "\"shasum\": \"$SHASUM\"," >>$JSONFILE - echo "\"size\": \"$BYTESIZE\"}" >>$JSONFILE - - s3cmd put -P --add-header="Cache-Control: max-age=0, must-revalidate" "$JSONFILE" "s3://ziglang.org/builds/$JSONFILE" - s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/$TRIPLEARCH-linux-$VERSION.json" - if [ "$GITBRANCH" = "master" ]; then - # avoid leaking oauth token - set +x - - cd "$BUILDDIR" - ./ci/srht/on_master_success "$VERSION" "$SRHT_OAUTH_TOKEN" - fi -fi diff --git a/zig/ci/drone/linux_script_base b/zig/ci/drone/linux_script_base new file mode 100755 index 0000000000..2788eb2df6 --- /dev/null +++ b/zig/ci/drone/linux_script_base @@ -0,0 +1,22 @@ +#!/bin/sh + +# https://docs.drone.io/pipeline/docker/syntax/workspace/ +# +# Drone automatically creates a temporary volume, known as your workspace, +# where it clones your repository. The workspace is the current working +# directory for each step in your pipeline. +# +# Because the workspace is a volume, filesystem changes are persisted between +# pipeline steps. In other words, individual steps can communicate and share +# state using the filesystem. +# +# Workspace volumes are ephemeral. They are created when the pipeline starts +# and destroyed after the pipeline completes. + +set -x +set -e + +TRIPLEARCH="$(uname -m)" +DISTDIR="$DRONE_WORKSPACE/dist" + +export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache" diff --git a/zig/ci/drone/linux_script_build b/zig/ci/drone/linux_script_build new file mode 100755 index 0000000000..3aedafbb89 --- /dev/null +++ b/zig/ci/drone/linux_script_build @@ -0,0 +1,18 @@ +#!/bin/sh + +. ./ci/drone/linux_script_base + +apk update +apk add samurai + +# Make the `zig version` number consistent. +# This will affect the cmake command below. +git config core.abbrev 9 +git fetch --unshallow || true +git fetch --tags + +mkdir build +cd build +cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_INSTALL_PREFIX=$DISTDIR" -DZIG_STATIC=ON -DCMAKE_PREFIX_PATH=/deps/local -GNinja + +samu install diff --git a/zig/ci/drone/linux_script_finalize b/zig/ci/drone/linux_script_finalize new file mode 100755 index 0000000000..da754a6b54 --- /dev/null +++ b/zig/ci/drone/linux_script_finalize @@ -0,0 +1,46 @@ +#!/bin/sh + +. ./ci/drone/linux_script_base + +if [ -n "$DRONE_PULL_REQUEST" ]; then + exit 0 +fi + +apk update +apk add py3-pip xz perl-utils jq curl samurai +pip3 install s3cmd + +cd build + +mv ../LICENSE "$DISTDIR/" +# docs are disabled due to: https://github.com/ziglang/zig/issues/8597 +#mv ../zig-cache/langref.html "$DISTDIR/" +mv "$DISTDIR/bin/zig" "$DISTDIR/" +rmdir "$DISTDIR/bin" + +GITBRANCH="$DRONE_BRANCH" +VERSION="$("$DISTDIR/zig" version)" +DIRNAME="zig-linux-$TRIPLEARCH-$VERSION" +TARBALL="$DIRNAME.tar.xz" +mv "$DISTDIR" "$DIRNAME" +tar cfJ "$TARBALL" "$DIRNAME" + +s3cmd put -P --add-header="cache-control: public, max-age=31536000, immutable" "$TARBALL" s3://ziglang.org/builds/ + +SHASUM=$(shasum -a 256 $TARBALL | cut '-d ' -f1) +BYTESIZE=$(wc -c < $TARBALL) + +JSONFILE="tarball.json" +touch $JSONFILE +echo "{\"tarball\": \"$TARBALL\"," >>$JSONFILE +echo "\"shasum\": \"$SHASUM\"," >>$JSONFILE +echo "\"size\": \"$BYTESIZE\"}" >>$JSONFILE + +s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/$TRIPLEARCH-linux-$VERSION.json" +if [ "$GITBRANCH" = "master" ]; then + # avoid leaking oauth token + set +x + + cd "$DRONE_WORKSPACE" + ./ci/srht/on_master_success "$VERSION" "$SRHT_OAUTH_TOKEN" +fi diff --git a/zig/ci/drone/linux_script_test b/zig/ci/drone/linux_script_test new file mode 100755 index 0000000000..a8e497a619 --- /dev/null +++ b/zig/ci/drone/linux_script_test @@ -0,0 +1,46 @@ +#!/bin/sh + +. ./ci/drone/linux_script_base + +# only release-fast builds of test suite due to: https://github.com/ziglang/zig/issues/8597 +# +# Some test suite components will be missing because they do not support +# forcing -OReleaseFast +# +# see `zig build --help` for the full list of test-* components +case "$1" in + 1) + steps="\ + test-stage2 \ + test-fmt \ + test-behavior" + ;; + 2) + steps="test-std" + ;; + 3) + steps="\ + test-compiler-rt \ + test-minilibc \ + test-compare-output \ + test-translate-c \ + test-run-translated-c" + ;; + '') + echo "error: expecting test group argument" + exit 1 + ;; + *) + echo "error: unknown test group: $1" + exit 1 + ;; +esac + +# only release-fast builds of test suite due to: https://github.com/ziglang/zig/issues/8597 +./build/zig build \ + -Drelease \ + -Dskip-debug \ + -Dskip-release-small \ + -Dskip-release-safe \ + -Dskip-non-native \ + $steps diff --git a/zig/ci/srht/freebsd_script b/zig/ci/srht/freebsd_script index 337d715b7c..4999ad7634 100755 --- a/zig/ci/srht/freebsd_script +++ b/zig/ci/srht/freebsd_script @@ -4,12 +4,11 @@ set -x set -e sudo pkg update -fq -sudo pkg install -y cmake py37-s3cmd wget curl jq +sudo pkg install -y cmake py38-s3cmd wget curl jq samurai ZIGDIR="$(pwd)" -CACHE_BASENAME="llvm+clang+lld-11.0.0-x86_64-freebsd-release" +CACHE_BASENAME="zig+llvm+lld+clang-x86_64-freebsd-gnu-0.8.0-dev.1939+5a3ea9bec" PREFIX="$HOME/$CACHE_BASENAME" -JOBS="-j$(sysctl -n hw.ncpu)" cd $HOME wget -nv "https://ziglang.org/deps/$CACHE_BASENAME.tar.xz" @@ -30,8 +29,14 @@ export TERM=dumb mkdir build cd build -cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=$PREFIX "-DCMAKE_INSTALL_PREFIX=$(pwd)/release" -DZIG_STATIC=ON -make $JOBS install +cmake .. \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH=$PREFIX \ + "-DCMAKE_INSTALL_PREFIX=$(pwd)/release" \ + -DZIG_STATIC=ON \ + -DZIG_TARGET_TRIPLE=x86_64-freebsd-gnu \ + -GNinja +samu install # Here we skip some tests to save time. release/bin/zig build test -Dskip-compile-errors -Dskip-non-native diff --git a/zig/doc/langref.html.in b/zig/doc/langref.html.in index 63e4c946b6..744d33f85c 100644 --- a/zig/doc/langref.html.in +++ b/zig/doc/langref.html.in @@ -2861,6 +2861,37 @@ fn dump(args: anytype) void { expect(args.b); expect(args.s[0] == 'h'); expect(args.s[1] == 'i'); +} + {#code_end#} +

+ Anonymous structs can be created without specifying field names, and are referred to as "tuples". +

+

+ The fields are implicitly named using numbers starting from 0. Because their names are integers, + the {#syntax#}@"0"{#endsyntax#} syntax must be used to access them. Names inside {#syntax#}@""{#endsyntax#} are always recognised as identifiers. +

+

+ Like arrays, tuples have a .len field, can be indexed and work with the ++ and ** operators. They can also be iterated over with {#link|inline for#}. +

+ {#code_begin|test|tuple#} +const std = @import("std"); +const expect = std.testing.expect; + +test "tuple" { + const values = .{ + @as(u32, 1234), + @as(f64, 12.34), + true, + "hi", + } ++ .{false} ** 2; + expect(values[0] == 1234); + expect(values[4] == false); + inline for (values) |v, i| { + if (i != 2) continue; + expect(v); + } + expect(values.len == 6); + expect(values.@"3"[0] == 'h'); } {#code_end#} {#header_close#} @@ -6528,7 +6559,7 @@ test "suspend with no resume" { fn func() void { x += 1; - suspend; + suspend {} // This line is never reached because the suspend has no matching resume. x += 1; } @@ -6593,7 +6624,7 @@ fn testResumeFromSuspend(my_result: *i32) void { resume @frame(); } my_result.* += 1; - suspend; + suspend {} my_result.* += 1; } {#code_end#} @@ -6632,7 +6663,7 @@ fn amain() void { } fn func() void { - suspend; + suspend {} } {#code_end#}

@@ -6934,7 +6965,7 @@ test "async fn pointer in a struct field" { fn func(y: *i32) void { defer y.* += 2; y.* += 1; - suspend; + suspend {} } {#code_end#} {#header_close#} @@ -7517,13 +7548,13 @@ test "main" { {#header_close#} {#header_open|@export#} -

{#syntax#}@export(target: anytype, comptime options: std.builtin.ExportOptions) void{#endsyntax#}
+
{#syntax#}@export(identifier, comptime options: std.builtin.ExportOptions) void{#endsyntax#}

Creates a symbol in the output object file.

This function can be called from a {#link|comptime#} block to conditionally export symbols. - When {#syntax#}target{#endsyntax#} is a function with the C calling convention and + When {#syntax#}identifier{#endsyntax#} is a function with the C calling convention and {#syntax#}options.linkage{#endsyntax#} is {#syntax#}Strong{#endsyntax#}, this is equivalent to the {#syntax#}export{#endsyntax#} keyword used on a function:

@@ -7550,6 +7581,14 @@ export fn @"A function name that is a complete sentence."() void {} {#see_also|Exporting a C Library#} {#header_close#} + {#header_open|@extern#} +
{#syntax#}@extern(T: type, comptime options: std.builtin.ExternOptions) *T{#endsyntax#}
+

+ Creates a reference to an external symbol in the output object file. +

+ {#see_also|@export#} + {#header_close#} + {#header_open|@fence#}
{#syntax#}@fence(order: AtomicOrder){#endsyntax#}

@@ -7659,7 +7698,7 @@ test "heap allocated frame" { } fn func() void { - suspend; + suspend {} } {#code_end#} {#header_close#} @@ -10008,7 +10047,7 @@ pub fn build(b: *Builder) void { {#code_end#}

terminal

$ zig build
-$ ./zig-cache/bin/test
+$ ./zig-out/bin/test
 all your base are belong to us
{#see_also|Targets|Zig Build System#} {#header_close#} diff --git a/zig/lib/std/Thread.zig b/zig/lib/std/Thread.zig index 7e8a6226e6..a85219a458 100644 --- a/zig/lib/std/Thread.zig +++ b/zig/lib/std/Thread.zig @@ -68,11 +68,30 @@ else switch (std.Target.current.os.tag) { }; /// Signals the processor that it is inside a busy-wait spin-loop ("spin lock"). -pub fn spinLoopHint() void { +pub fn spinLoopHint() callconv(.Inline) void { switch (std.Target.current.cpu.arch) { - .i386, .x86_64 => asm volatile ("pause" ::: "memory"), - .arm, .aarch64 => asm volatile ("yield" ::: "memory"), - else => {}, + .i386, .x86_64 => { + asm volatile ("pause" ::: "memory"); + }, + .arm, .armeb, .thumb, .thumbeb => { + // `yield` was introduced in v6k but are also available on v6m. + const can_yield = comptime std.Target.arm.featureSetHasAny(std.Target.current.cpu.features, .{ .has_v6k, .has_v6m }); + if (can_yield) asm volatile ("yield" ::: "memory") + // Fallback. + else asm volatile ("" ::: "memory"); + }, + .aarch64, .aarch64_be, .aarch64_32 => { + asm volatile ("isb" ::: "memory"); + }, + .powerpc64, .powerpc64le => { + // No-op that serves as `yield` hint. + asm volatile ("or 27, 27, 27" ::: "memory"); + }, + else => { + // Do nothing but prevent the compiler from optimizing away the + // spinning loop. + asm volatile ("" ::: "memory"); + }, } } @@ -199,7 +218,8 @@ pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startF inner: Context, }; fn threadMain(raw_arg: windows.LPVOID) callconv(.C) windows.DWORD { - const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*; + const arg = if (@sizeOf(Context) == 0) undefined // + else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*; switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) { .NoReturn => { @@ -260,7 +280,8 @@ pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startF const MainFuncs = struct { fn linuxThreadMain(ctx_addr: usize) callconv(.C) u8 { - const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*; + const arg = if (@sizeOf(Context) == 0) undefined // + else @intToPtr(*Context, ctx_addr).*; switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) { .NoReturn => { @@ -292,7 +313,8 @@ pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startF } } fn posixThreadMain(ctx: ?*c_void) callconv(.C) ?*c_void { - const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), ctx)).*; + const arg = if (@sizeOf(Context) == 0) undefined // + else @ptrCast(*Context, @alignCast(@alignOf(Context), ctx)).*; switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) { .NoReturn => { diff --git a/zig/lib/std/array_list.zig b/zig/lib/std/array_list.zig index f30a86d8f7..7825d36ac3 100644 --- a/zig/lib/std/array_list.zig +++ b/zig/lib/std/array_list.zig @@ -50,7 +50,6 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { allocator: *Allocator, pub const Slice = if (alignment) |a| ([]align(a) T) else []T; - pub const SliceConst = if (alignment) |a| ([]align(a) const T) else []const T; /// Deinitialize with `deinit` or use `toOwnedSlice`. pub fn init(allocator: *Allocator) Self { @@ -141,7 +140,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { /// Insert slice `items` at index `i` by moving `list[i .. list.len]` to make room. /// This operation is O(N). - pub fn insertSlice(self: *Self, i: usize, items: SliceConst) !void { + pub fn insertSlice(self: *Self, i: usize, items: []const T) !void { try self.ensureCapacity(self.items.len + items.len); self.items.len += items.len; @@ -153,7 +152,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { /// Grows list if `len < new_items.len`. /// Shrinks list if `len > new_items.len`. /// Invalidates pointers if this ArrayList is resized. - pub fn replaceRange(self: *Self, start: usize, len: usize, new_items: SliceConst) !void { + pub fn replaceRange(self: *Self, start: usize, len: usize, new_items: []const T) !void { const after_range = start + len; const range = self.items[start..after_range]; @@ -220,14 +219,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { /// Append the slice of items to the list. Allocates more /// memory as necessary. - pub fn appendSlice(self: *Self, items: SliceConst) !void { + pub fn appendSlice(self: *Self, items: []const T) !void { try self.ensureCapacity(self.items.len + items.len); self.appendSliceAssumeCapacity(items); } /// Append the slice of items to the list, asserting the capacity is already /// enough to store the new items. **Does not** invalidate pointers. - pub fn appendSliceAssumeCapacity(self: *Self, items: SliceConst) void { + pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void { const oldlen = self.items.len; const newlen = self.items.len + items.len; self.items.len = newlen; @@ -429,7 +428,6 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ capacity: usize = 0, pub const Slice = if (alignment) |a| ([]align(a) T) else []T; - pub const SliceConst = if (alignment) |a| ([]align(a) const T) else []const T; /// Initialize with capacity to hold at least num elements. /// Deinitialize with `deinit` or use `toOwnedSlice`. @@ -483,7 +481,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Insert slice `items` at index `i`. Moves `list[i .. list.len]` to /// higher indicices make room. /// This operation is O(N). - pub fn insertSlice(self: *Self, allocator: *Allocator, i: usize, items: SliceConst) !void { + pub fn insertSlice(self: *Self, allocator: *Allocator, i: usize, items: []const T) !void { try self.ensureCapacity(allocator, self.items.len + items.len); self.items.len += items.len; @@ -495,7 +493,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Grows list if `len < new_items.len`. /// Shrinks list if `len > new_items.len` /// Invalidates pointers if this ArrayList is resized. - pub fn replaceRange(self: *Self, allocator: *Allocator, start: usize, len: usize, new_items: SliceConst) !void { + pub fn replaceRange(self: *Self, allocator: *Allocator, start: usize, len: usize, new_items: []const T) !void { var managed = self.toManaged(allocator); try managed.replaceRange(start, len, new_items); self.* = managed.toUnmanaged(); @@ -543,14 +541,14 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Append the slice of items to the list. Allocates more /// memory as necessary. - pub fn appendSlice(self: *Self, allocator: *Allocator, items: SliceConst) !void { + pub fn appendSlice(self: *Self, allocator: *Allocator, items: []const T) !void { try self.ensureCapacity(allocator, self.items.len + items.len); self.appendSliceAssumeCapacity(items); } /// Append the slice of items to the list, asserting the capacity is enough /// to store the new items. - pub fn appendSliceAssumeCapacity(self: *Self, items: SliceConst) void { + pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void { const oldlen = self.items.len; const newlen = self.items.len + items.len; @@ -1128,15 +1126,31 @@ test "std.ArrayList/ArrayListUnmanaged: ArrayList(T) of struct T" { } } -test "std.ArrayList(u8) implements writer" { - var buffer = ArrayList(u8).init(std.testing.allocator); - defer buffer.deinit(); +test "std.ArrayList(u8)/ArrayListAligned implements writer" { + const a = testing.allocator; + + { + var buffer = ArrayList(u8).init(a); + defer buffer.deinit(); + + const x: i32 = 42; + const y: i32 = 1234; + try buffer.writer().print("x: {}\ny: {}\n", .{ x, y }); - const x: i32 = 42; - const y: i32 = 1234; - try buffer.writer().print("x: {}\ny: {}\n", .{ x, y }); + testing.expectEqualSlices(u8, "x: 42\ny: 1234\n", buffer.items); + } + { + var list = ArrayListAligned(u8, 2).init(a); + defer list.deinit(); - testing.expectEqualSlices(u8, "x: 42\ny: 1234\n", buffer.items); + const writer = list.writer(); + try writer.writeAll("a"); + try writer.writeAll("bc"); + try writer.writeAll("d"); + try writer.writeAll("efg"); + + testing.expectEqualSlices(u8, list.items, "abcdefg"); + } } test "std.ArrayList/ArrayListUnmanaged.shrink still sets length on error.OutOfMemory" { @@ -1167,18 +1181,6 @@ test "std.ArrayList/ArrayListUnmanaged.shrink still sets length on error.OutOfMe } } -test "std.ArrayList.writer" { - var list = ArrayList(u8).init(std.testing.allocator); - defer list.deinit(); - - const writer = list.writer(); - try writer.writeAll("a"); - try writer.writeAll("bc"); - try writer.writeAll("d"); - try writer.writeAll("efg"); - testing.expectEqualSlices(u8, list.items, "abcdefg"); -} - test "std.ArrayList/ArrayListUnmanaged.addManyAsArray" { const a = std.testing.allocator; { @@ -1226,3 +1228,27 @@ test "std.ArrayList/ArrayListUnmanaged.toOwnedSliceSentinel" { testing.expectEqualStrings(result, mem.spanZ(result.ptr)); } } + +test "ArrayListAligned/ArrayListAlignedUnmanaged accepts unaligned slices" { + const a = testing.allocator; + { + var list = std.ArrayListAligned(u8, 8).init(a); + defer list.deinit(); + + try list.appendSlice(&.{ 0, 1, 2, 3 }); + try list.insertSlice(2, &.{ 4, 5, 6, 7 }); + try list.replaceRange(1, 3, &.{ 8, 9 }); + + testing.expectEqualSlices(u8, list.items, &.{ 0, 8, 9, 6, 7, 2, 3 }); + } + { + var list = std.ArrayListAlignedUnmanaged(u8, 8){}; + defer list.deinit(a); + + try list.appendSlice(a, &.{ 0, 1, 2, 3 }); + try list.insertSlice(a, 2, &.{ 4, 5, 6, 7 }); + try list.replaceRange(a, 1, 3, &.{ 8, 9 }); + + testing.expectEqualSlices(u8, list.items, &.{ 0, 8, 9, 6, 7, 2, 3 }); + } +} diff --git a/zig/lib/std/atomic/bool.zig b/zig/lib/std/atomic/bool.zig index c968b862b9..0cffb99d38 100644 --- a/zig/lib/std/atomic/bool.zig +++ b/zig/lib/std/atomic/bool.zig @@ -28,7 +28,7 @@ pub const Bool = extern struct { return @atomicRmw(bool, &self.unprotected_value, .Xchg, operand, ordering); } - pub fn load(self: *Self, comptime ordering: std.builtin.AtomicOrder) bool { + pub fn load(self: *const Self, comptime ordering: std.builtin.AtomicOrder) bool { switch (ordering) { .Unordered, .Monotonic, .Acquire, .SeqCst => {}, else => @compileError("Invalid ordering '" ++ @tagName(ordering) ++ "' for a load operation"), diff --git a/zig/lib/std/atomic/int.zig b/zig/lib/std/atomic/int.zig index 1a3bead2df..2d1c5f80e9 100644 --- a/zig/lib/std/atomic/int.zig +++ b/zig/lib/std/atomic/int.zig @@ -31,7 +31,7 @@ pub fn Int(comptime T: type) type { return @atomicRmw(T, &self.unprotected_value, op, operand, ordering); } - pub fn load(self: *Self, comptime ordering: builtin.AtomicOrder) T { + pub fn load(self: *const Self, comptime ordering: builtin.AtomicOrder) T { switch (ordering) { .Unordered, .Monotonic, .Acquire, .SeqCst => {}, else => @compileError("Invalid ordering '" ++ @tagName(ordering) ++ "' for a load operation"), @@ -59,7 +59,7 @@ pub fn Int(comptime T: type) type { return self.rmw(.Sub, 1, .SeqCst); } - pub fn get(self: *Self) T { + pub fn get(self: *const Self) T { return self.load(.SeqCst); } diff --git a/zig/lib/std/build.zig b/zig/lib/std/build.zig index a652de9c12..1811073279 100644 --- a/zig/lib/std/build.zig +++ b/zig/lib/std/build.zig @@ -195,7 +195,8 @@ pub const Builder = struct { self.install_prefix = install_prefix orelse "/usr"; self.install_path = fs.path.join(self.allocator, &[_][]const u8{ dest_dir, self.install_prefix }) catch unreachable; } else { - self.install_prefix = install_prefix orelse self.cache_root; + self.install_prefix = install_prefix orelse + (fs.path.join(self.allocator, &[_][]const u8{ self.build_root, "zig-out" }) catch unreachable); self.install_path = self.install_prefix; } self.lib_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "lib" }) catch unreachable; @@ -675,17 +676,19 @@ pub const Builder = struct { /// Exposes standard `zig build` options for choosing a target. pub fn standardTargetOptions(self: *Builder, args: StandardTargetOptionsArgs) CrossTarget { - const triple = self.option( + const maybe_triple = self.option( []const u8, "target", "The CPU architecture, OS, and ABI to build for", - ) orelse return args.default_target; + ); + const mcpu = self.option([]const u8, "cpu", "Target CPU"); - // TODO add cpu and features as part of the target triple + const triple = maybe_triple orelse return args.default_target; var diags: CrossTarget.ParseOptions.Diagnostics = .{}; const selected_target = CrossTarget.parse(.{ .arch_os_abi = triple, + .cpu_features = mcpu, .diagnostics = &diags, }) catch |err| switch (err) { error.UnknownCpuModel => { @@ -1384,6 +1387,8 @@ pub const LibExeObjStep = struct { /// safely garbage-collected during the linking phase. link_function_sections: bool = false, + linker_allow_shlib_undefined: ?bool = null, + /// Uses system Wine installation to run cross compiled Windows build artifacts. enable_wine: bool = false, @@ -2336,6 +2341,9 @@ pub const LibExeObjStep = struct { if (self.link_function_sections) { try zig_args.append("-ffunction-sections"); } + if (self.linker_allow_shlib_undefined) |x| { + try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined"); + } if (self.single_threaded) { try zig_args.append("--single-threaded"); } diff --git a/zig/lib/std/build/run.zig b/zig/lib/std/build/run.zig index ca39b0216e..1e16813cb8 100644 --- a/zig/lib/std/build/run.zig +++ b/zig/lib/std/build/run.zig @@ -191,6 +191,13 @@ pub const RunStep = struct { child.stdout_behavior = stdIoActionToBehavior(self.stdout_action); child.stderr_behavior = stdIoActionToBehavior(self.stderr_action); + if (self.builder.verbose) { + for (argv) |arg| { + warn("{s} ", .{arg}); + } + warn("\n", .{}); + } + child.spawn() catch |err| { warn("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) }); return err; diff --git a/zig/lib/std/builtin.zig b/zig/lib/std/builtin.zig index 93de8ae3b9..53c27f0bf5 100644 --- a/zig/lib/std/builtin.zig +++ b/zig/lib/std/builtin.zig @@ -165,6 +165,7 @@ pub const CallingConvention = enum { APCS, AAPCS, AAPCSVFP, + SysV, }; /// This data structure is used by the Zig language code generation and diff --git a/zig/lib/std/c.zig b/zig/lib/std/c.zig index 01247ffc00..f66376f812 100644 --- a/zig/lib/std/c.zig +++ b/zig/lib/std/c.zig @@ -10,8 +10,6 @@ const page_size = std.mem.page_size; pub const tokenizer = @import("c/tokenizer.zig"); pub const Token = tokenizer.Token; pub const Tokenizer = tokenizer.Tokenizer; -pub const parse = @import("c/parse.zig").parse; -pub const ast = @import("c/ast.zig"); pub const builtins = @import("c/builtins.zig"); test { diff --git a/zig/lib/std/c/ast.zig b/zig/lib/std/c/ast.zig deleted file mode 100644 index 71455c0ea3..0000000000 --- a/zig/lib/std/c/ast.zig +++ /dev/null @@ -1,681 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copyright (c) 2015-2021 Zig Contributors -// This file is part of [zig](https://ziglang.org/), which is MIT licensed. -// The MIT license requires this copyright notice to be included in all copies -// and substantial portions of the software. -const std = @import("std"); -const ArrayList = std.ArrayList; -const Token = std.c.Token; -const Source = std.c.tokenizer.Source; - -pub const TokenIndex = usize; - -pub const Tree = struct { - tokens: []Token, - sources: []Source, - root_node: *Node.Root, - arena_state: std.heap.ArenaAllocator.State, - gpa: *mem.Allocator, - msgs: []Msg, - - pub fn deinit(self: *Tree) void { - self.arena_state.promote(self.gpa).deinit(); - } - - pub fn tokenSlice(tree: *Tree, token: TokenIndex) []const u8 { - return tree.tokens.at(token).slice(); - } - - pub fn tokenEql(tree: *Tree, a: TokenIndex, b: TokenIndex) bool { - const atok = tree.tokens.at(a); - const btok = tree.tokens.at(b); - return atok.eql(btok.*); - } -}; - -pub const Msg = struct { - kind: enum { - Error, - Warning, - Note, - }, - inner: Error, -}; - -pub const Error = union(enum) { - InvalidToken: SingleTokenError("invalid token '{}'"), - ExpectedToken: ExpectedToken, - ExpectedExpr: SingleTokenError("expected expression, found '{}'"), - ExpectedTypeName: SingleTokenError("expected type name, found '{}'"), - ExpectedFnBody: SingleTokenError("expected function body, found '{}'"), - ExpectedDeclarator: SingleTokenError("expected declarator, found '{}'"), - ExpectedInitializer: SingleTokenError("expected initializer, found '{}'"), - ExpectedEnumField: SingleTokenError("expected enum field, found '{}'"), - ExpectedType: SingleTokenError("expected enum field, found '{}'"), - InvalidTypeSpecifier: InvalidTypeSpecifier, - InvalidStorageClass: SingleTokenError("invalid storage class, found '{}'"), - InvalidDeclarator: SimpleError("invalid declarator"), - DuplicateQualifier: SingleTokenError("duplicate type qualifier '{}'"), - DuplicateSpecifier: SingleTokenError("duplicate declaration specifier '{}'"), - MustUseKwToRefer: MustUseKwToRefer, - FnSpecOnNonFn: SingleTokenError("function specifier '{}' on non function"), - NothingDeclared: SimpleError("declaration doesn't declare anything"), - QualifierIgnored: SingleTokenError("qualifier '{}' ignored"), - - pub fn render(self: *const Error, tree: *Tree, stream: anytype) !void { - switch (self.*) { - .InvalidToken => |*x| return x.render(tree, stream), - .ExpectedToken => |*x| return x.render(tree, stream), - .ExpectedExpr => |*x| return x.render(tree, stream), - .ExpectedTypeName => |*x| return x.render(tree, stream), - .ExpectedDeclarator => |*x| return x.render(tree, stream), - .ExpectedFnBody => |*x| return x.render(tree, stream), - .ExpectedInitializer => |*x| return x.render(tree, stream), - .ExpectedEnumField => |*x| return x.render(tree, stream), - .ExpectedType => |*x| return x.render(tree, stream), - .InvalidTypeSpecifier => |*x| return x.render(tree, stream), - .InvalidStorageClass => |*x| return x.render(tree, stream), - .InvalidDeclarator => |*x| return x.render(tree, stream), - .DuplicateQualifier => |*x| return x.render(tree, stream), - .DuplicateSpecifier => |*x| return x.render(tree, stream), - .MustUseKwToRefer => |*x| return x.render(tree, stream), - .FnSpecOnNonFn => |*x| return x.render(tree, stream), - .NothingDeclared => |*x| return x.render(tree, stream), - .QualifierIgnored => |*x| return x.render(tree, stream), - } - } - - pub fn loc(self: *const Error) TokenIndex { - switch (self.*) { - .InvalidToken => |x| return x.token, - .ExpectedToken => |x| return x.token, - .ExpectedExpr => |x| return x.token, - .ExpectedTypeName => |x| return x.token, - .ExpectedDeclarator => |x| return x.token, - .ExpectedFnBody => |x| return x.token, - .ExpectedInitializer => |x| return x.token, - .ExpectedEnumField => |x| return x.token, - .ExpectedType => |*x| return x.token, - .InvalidTypeSpecifier => |x| return x.token, - .InvalidStorageClass => |x| return x.token, - .InvalidDeclarator => |x| return x.token, - .DuplicateQualifier => |x| return x.token, - .DuplicateSpecifier => |x| return x.token, - .MustUseKwToRefer => |*x| return x.name, - .FnSpecOnNonFn => |*x| return x.name, - .NothingDeclared => |*x| return x.name, - .QualifierIgnored => |*x| return x.name, - } - } - - pub const ExpectedToken = struct { - token: TokenIndex, - expected_id: std.meta.Tag(Token.Id), - - pub fn render(self: *const ExpectedToken, tree: *Tree, stream: anytype) !void { - const found_token = tree.tokens.at(self.token); - if (found_token.id == .Invalid) { - return stream.print("expected '{s}', found invalid bytes", .{self.expected_id.symbol()}); - } else { - const token_name = found_token.id.symbol(); - return stream.print("expected '{s}', found '{s}'", .{ self.expected_id.symbol(), token_name }); - } - } - }; - - pub const InvalidTypeSpecifier = struct { - token: TokenIndex, - type_spec: *Node.TypeSpec, - - pub fn render(self: *const ExpectedToken, tree: *Tree, stream: anytype) !void { - try stream.write("invalid type specifier '"); - try type_spec.spec.print(tree, stream); - const token_name = tree.tokens.at(self.token).id.symbol(); - return stream.print("{s}'", .{token_name}); - } - }; - - pub const MustUseKwToRefer = struct { - kw: TokenIndex, - name: TokenIndex, - - pub fn render(self: *const ExpectedToken, tree: *Tree, stream: anytype) !void { - return stream.print("must use '{s}' tag to refer to type '{s}'", .{ tree.slice(kw), tree.slice(name) }); - } - }; - - fn SingleTokenError(comptime msg: []const u8) type { - return struct { - token: TokenIndex, - - pub fn render(self: *const @This(), tree: *Tree, stream: anytype) !void { - const actual_token = tree.tokens.at(self.token); - return stream.print(msg, .{actual_token.id.symbol()}); - } - }; - } - - fn SimpleError(comptime msg: []const u8) type { - return struct { - const ThisError = @This(); - - token: TokenIndex, - - pub fn render(self: *const ThisError, tokens: *Tree.TokenList, stream: anytype) !void { - return stream.write(msg); - } - }; - } -}; - -pub const Type = struct { - pub const TypeList = ArrayList(*Type); - - @"const": bool = false, - atomic: bool = false, - @"volatile": bool = false, - restrict: bool = false, - - id: union(enum) { - Int: struct { - id: Id, - is_signed: bool, - - pub const Id = enum { - Char, - Short, - Int, - Long, - LongLong, - }; - }, - Float: struct { - id: Id, - - pub const Id = enum { - Float, - Double, - LongDouble, - }; - }, - Pointer: *Type, - Function: struct { - return_type: *Type, - param_types: TypeList, - }, - Typedef: *Type, - Record: *Node.RecordType, - Enum: *Node.EnumType, - - /// Special case for macro parameters that can be any type. - /// Only present if `retain_macros == true`. - Macro, - }, -}; - -pub const Node = struct { - id: Id, - - pub const Id = enum { - Root, - EnumField, - RecordField, - RecordDeclarator, - JumpStmt, - ExprStmt, - LabeledStmt, - CompoundStmt, - IfStmt, - SwitchStmt, - WhileStmt, - DoStmt, - ForStmt, - StaticAssert, - Declarator, - Pointer, - FnDecl, - Typedef, - VarDecl, - }; - - pub const Root = struct { - base: Node = Node{ .id = .Root }, - decls: DeclList, - eof: TokenIndex, - - pub const DeclList = ArrayList(*Node); - }; - - pub const DeclSpec = struct { - storage_class: union(enum) { - Auto: TokenIndex, - Extern: TokenIndex, - Register: TokenIndex, - Static: TokenIndex, - Typedef: TokenIndex, - None, - } = .None, - thread_local: ?TokenIndex = null, - type_spec: TypeSpec = TypeSpec{}, - fn_spec: union(enum) { - Inline: TokenIndex, - Noreturn: TokenIndex, - None, - } = .None, - align_spec: ?struct { - alignas: TokenIndex, - expr: *Node, - rparen: TokenIndex, - } = null, - }; - - pub const TypeSpec = struct { - qual: TypeQual = TypeQual{}, - spec: union(enum) { - /// error or default to int - None, - Void: TokenIndex, - Char: struct { - sign: ?TokenIndex = null, - char: TokenIndex, - }, - Short: struct { - sign: ?TokenIndex = null, - short: TokenIndex = null, - int: ?TokenIndex = null, - }, - Int: struct { - sign: ?TokenIndex = null, - int: ?TokenIndex = null, - }, - Long: struct { - sign: ?TokenIndex = null, - long: TokenIndex, - longlong: ?TokenIndex = null, - int: ?TokenIndex = null, - }, - Float: struct { - float: TokenIndex, - complex: ?TokenIndex = null, - }, - Double: struct { - long: ?TokenIndex = null, - double: ?TokenIndex, - complex: ?TokenIndex = null, - }, - Bool: TokenIndex, - Atomic: struct { - atomic: TokenIndex, - typename: *Node, - rparen: TokenIndex, - }, - Enum: *EnumType, - Record: *RecordType, - Typedef: struct { - sym: TokenIndex, - sym_type: *Type, - }, - - pub fn print(self: *@This(), self: *const @This(), tree: *Tree, stream: anytype) !void { - switch (self.spec) { - .None => unreachable, - .Void => |index| try stream.write(tree.slice(index)), - .Char => |char| { - if (char.sign) |s| { - try stream.write(tree.slice(s)); - try stream.writeByte(' '); - } - try stream.write(tree.slice(char.char)); - }, - .Short => |short| { - if (short.sign) |s| { - try stream.write(tree.slice(s)); - try stream.writeByte(' '); - } - try stream.write(tree.slice(short.short)); - if (short.int) |i| { - try stream.writeByte(' '); - try stream.write(tree.slice(i)); - } - }, - .Int => |int| { - if (int.sign) |s| { - try stream.write(tree.slice(s)); - try stream.writeByte(' '); - } - if (int.int) |i| { - try stream.writeByte(' '); - try stream.write(tree.slice(i)); - } - }, - .Long => |long| { - if (long.sign) |s| { - try stream.write(tree.slice(s)); - try stream.writeByte(' '); - } - try stream.write(tree.slice(long.long)); - if (long.longlong) |l| { - try stream.writeByte(' '); - try stream.write(tree.slice(l)); - } - if (long.int) |i| { - try stream.writeByte(' '); - try stream.write(tree.slice(i)); - } - }, - .Float => |float| { - try stream.write(tree.slice(float.float)); - if (float.complex) |c| { - try stream.writeByte(' '); - try stream.write(tree.slice(c)); - } - }, - .Double => |double| { - if (double.long) |l| { - try stream.write(tree.slice(l)); - try stream.writeByte(' '); - } - try stream.write(tree.slice(double.double)); - if (double.complex) |c| { - try stream.writeByte(' '); - try stream.write(tree.slice(c)); - } - }, - .Bool => |index| try stream.write(tree.slice(index)), - .Typedef => |typedef| try stream.write(tree.slice(typedef.sym)), - else => try stream.print("TODO print {}", self.spec), - } - } - } = .None, - }; - - pub const EnumType = struct { - tok: TokenIndex, - name: ?TokenIndex, - body: ?struct { - lbrace: TokenIndex, - - /// always EnumField - fields: FieldList, - rbrace: TokenIndex, - }, - - pub const FieldList = Root.DeclList; - }; - - pub const EnumField = struct { - base: Node = Node{ .id = .EnumField }, - name: TokenIndex, - value: ?*Node, - }; - - pub const RecordType = struct { - tok: TokenIndex, - kind: enum { - Struct, - Union, - }, - name: ?TokenIndex, - body: ?struct { - lbrace: TokenIndex, - - /// RecordField or StaticAssert - fields: FieldList, - rbrace: TokenIndex, - }, - - pub const FieldList = Root.DeclList; - }; - - pub const RecordField = struct { - base: Node = Node{ .id = .RecordField }, - type_spec: TypeSpec, - declarators: DeclaratorList, - semicolon: TokenIndex, - - pub const DeclaratorList = Root.DeclList; - }; - - pub const RecordDeclarator = struct { - base: Node = Node{ .id = .RecordDeclarator }, - declarator: ?*Declarator, - bit_field_expr: ?*Expr, - }; - - pub const TypeQual = struct { - @"const": ?TokenIndex = null, - atomic: ?TokenIndex = null, - @"volatile": ?TokenIndex = null, - restrict: ?TokenIndex = null, - }; - - pub const JumpStmt = struct { - base: Node = Node{ .id = .JumpStmt }, - ltoken: TokenIndex, - kind: union(enum) { - Break, - Continue, - Return: ?*Node, - Goto: TokenIndex, - }, - semicolon: TokenIndex, - }; - - pub const ExprStmt = struct { - base: Node = Node{ .id = .ExprStmt }, - expr: ?*Expr, - semicolon: TokenIndex, - }; - - pub const LabeledStmt = struct { - base: Node = Node{ .id = .LabeledStmt }, - kind: union(enum) { - Label: TokenIndex, - Case: TokenIndex, - Default: TokenIndex, - }, - stmt: *Node, - }; - - pub const CompoundStmt = struct { - base: Node = Node{ .id = .CompoundStmt }, - lbrace: TokenIndex, - statements: StmtList, - rbrace: TokenIndex, - - pub const StmtList = Root.DeclList; - }; - - pub const IfStmt = struct { - base: Node = Node{ .id = .IfStmt }, - @"if": TokenIndex, - cond: *Node, - body: *Node, - @"else": ?struct { - tok: TokenIndex, - body: *Node, - }, - }; - - pub const SwitchStmt = struct { - base: Node = Node{ .id = .SwitchStmt }, - @"switch": TokenIndex, - expr: *Expr, - rparen: TokenIndex, - stmt: *Node, - }; - - pub const WhileStmt = struct { - base: Node = Node{ .id = .WhileStmt }, - @"while": TokenIndex, - cond: *Expr, - rparen: TokenIndex, - body: *Node, - }; - - pub const DoStmt = struct { - base: Node = Node{ .id = .DoStmt }, - do: TokenIndex, - body: *Node, - @"while": TokenIndex, - cond: *Expr, - semicolon: TokenIndex, - }; - - pub const ForStmt = struct { - base: Node = Node{ .id = .ForStmt }, - @"for": TokenIndex, - init: ?*Node, - cond: ?*Expr, - semicolon: TokenIndex, - incr: ?*Expr, - rparen: TokenIndex, - body: *Node, - }; - - pub const StaticAssert = struct { - base: Node = Node{ .id = .StaticAssert }, - assert: TokenIndex, - expr: *Node, - semicolon: TokenIndex, - }; - - pub const Declarator = struct { - base: Node = Node{ .id = .Declarator }, - pointer: ?*Pointer, - prefix: union(enum) { - None, - Identifer: TokenIndex, - Complex: struct { - lparen: TokenIndex, - inner: *Node, - rparen: TokenIndex, - }, - }, - suffix: union(enum) { - None, - Fn: struct { - lparen: TokenIndex, - params: Params, - rparen: TokenIndex, - }, - Array: Arrays, - }, - - pub const Arrays = ArrayList(*Array); - pub const Params = ArrayList(*Param); - }; - - pub const Array = struct { - lbracket: TokenIndex, - inner: union(enum) { - Inferred, - Unspecified: TokenIndex, - Variable: struct { - asterisk: ?TokenIndex, - static: ?TokenIndex, - qual: TypeQual, - expr: *Expr, - }, - }, - rbracket: TokenIndex, - }; - - pub const Pointer = struct { - base: Node = Node{ .id = .Pointer }, - asterisk: TokenIndex, - qual: TypeQual, - pointer: ?*Pointer, - }; - - pub const Param = struct { - kind: union(enum) { - Variable, - Old: TokenIndex, - Normal: struct { - decl_spec: *DeclSpec, - declarator: *Node, - }, - }, - }; - - pub const FnDecl = struct { - base: Node = Node{ .id = .FnDecl }, - decl_spec: DeclSpec, - declarator: *Declarator, - old_decls: OldDeclList, - body: ?*CompoundStmt, - - pub const OldDeclList = ArrayList(*Node); - }; - - pub const Typedef = struct { - base: Node = Node{ .id = .Typedef }, - decl_spec: DeclSpec, - declarators: DeclaratorList, - semicolon: TokenIndex, - - pub const DeclaratorList = Root.DeclList; - }; - - pub const VarDecl = struct { - base: Node = Node{ .id = .VarDecl }, - decl_spec: DeclSpec, - initializers: Initializers, - semicolon: TokenIndex, - - pub const Initializers = Root.DeclList; - }; - - pub const Initialized = struct { - base: Node = Node{ .id = Initialized }, - declarator: *Declarator, - eq: TokenIndex, - init: Initializer, - }; - - pub const Initializer = union(enum) { - list: struct { - initializers: List, - rbrace: TokenIndex, - }, - expr: *Expr, - - pub const List = ArrayList(*Initializer); - }; - - pub const Macro = struct { - base: Node = Node{ .id = Macro }, - kind: union(enum) { - Undef: []const u8, - Fn: struct { - params: []const []const u8, - expr: *Expr, - }, - Expr: *Expr, - }, - }; -}; - -pub const Expr = struct { - id: Id, - ty: *Type, - value: union(enum) { - None, - }, - - pub const Id = enum { - Infix, - Literal, - }; - - pub const Infix = struct { - base: Expr = Expr{ .id = .Infix }, - lhs: *Expr, - op_token: TokenIndex, - op: Op, - rhs: *Expr, - - pub const Op = enum {}; - }; -}; diff --git a/zig/lib/std/c/haiku.zig b/zig/lib/std/c/haiku.zig index e361a7520e..1aa3ac31e2 100644 --- a/zig/lib/std/c/haiku.zig +++ b/zig/lib/std/c/haiku.zig @@ -69,3 +69,51 @@ pub const pthread_rwlock_t = extern struct { writer_count: i32 = 0, waiters: [2]?*c_void = [_]?*c_void{ null, null }, }; + +pub const EAI = extern enum(c_int) { + /// address family for hostname not supported + ADDRFAMILY = 1, + + /// name could not be resolved at this time + AGAIN = 2, + + /// flags parameter had an invalid value + BADFLAGS = 3, + + /// non-recoverable failure in name resolution + FAIL = 4, + + /// address family not recognized + FAMILY = 5, + + /// memory allocation failure + MEMORY = 6, + + /// no address associated with hostname + NODATA = 7, + + /// name does not resolve + NONAME = 8, + + /// service not recognized for socket type + SERVICE = 9, + + /// intended socket type was not recognized + SOCKTYPE = 10, + + /// system error returned in errno + SYSTEM = 11, + + /// invalid value for hints + BADHINTS = 12, + + /// resolved protocol is unknown + PROTOCOL = 13, + + /// argument buffer overflow + OVERFLOW = 14, + + _, +}; + +pub const EAI_MAX = 15; diff --git a/zig/lib/std/c/parse.zig b/zig/lib/std/c/parse.zig deleted file mode 100644 index 29d4ba2fe1..0000000000 --- a/zig/lib/std/c/parse.zig +++ /dev/null @@ -1,1434 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copyright (c) 2015-2021 Zig Contributors -// This file is part of [zig](https://ziglang.org/), which is MIT licensed. -// The MIT license requires this copyright notice to be included in all copies -// and substantial portions of the software. -const std = @import("std"); -const mem = std.mem; -const assert = std.debug.assert; -const Allocator = std.mem.Allocator; -const ast = std.c.ast; -const Node = ast.Node; -const Type = ast.Type; -const Tree = ast.Tree; -const TokenIndex = ast.TokenIndex; -const Token = std.c.Token; -const TokenIterator = ast.Tree.TokenList.Iterator; - -pub const Error = error{ParseError} || Allocator.Error; - -pub const Options = struct { - // /// Keep simple macros unexpanded and add the definitions to the ast - // retain_macros: bool = false, - /// Warning or error - warn_as_err: union(enum) { - /// All warnings are warnings - None, - - /// Some warnings are errors - Some: []std.meta.Tag(ast.Error), - - /// All warnings are errors - All, - } = .All, -}; - -/// Result should be freed with tree.deinit() when there are -/// no more references to any of the tokens or nodes. -pub fn parse(allocator: *Allocator, source: []const u8, options: Options) !*Tree { - const tree = blk: { - // This block looks unnecessary, but is a "foot-shield" to prevent the SegmentedLists - // from being initialized with a pointer to this `arena`, which is created on - // the stack. Following code should instead refer to `&tree.arena_allocator`, a - // pointer to data which lives safely on the heap and will outlive `parse`. - var arena = std.heap.ArenaAllocator.init(allocator); - errdefer arena.deinit(); - const tree = try arena.allocator.create(ast.Tree); - tree.* = .{ - .root_node = undefined, - .arena_allocator = arena, - .tokens = undefined, - .sources = undefined, - }; - break :blk tree; - }; - errdefer tree.deinit(); - const arena = &tree.arena_allocator.allocator; - - tree.tokens = ast.Tree.TokenList.init(arena); - tree.sources = ast.Tree.SourceList.init(arena); - - var tokenizer = std.zig.Tokenizer.init(source); - while (true) { - const tree_token = try tree.tokens.addOne(); - tree_token.* = tokenizer.next(); - if (tree_token.id == .Eof) break; - } - // TODO preprocess here - var it = tree.tokens.iterator(0); - - while (true) { - const tok = it.peek().?.id; - switch (id) { - .LineComment, - .MultiLineComment, - => { - _ = it.next(); - }, - else => break, - } - } - - var parse_arena = std.heap.ArenaAllocator.init(allocator); - defer parse_arena.deinit(); - - var parser = Parser{ - .scopes = Parser.SymbolList.init(allocator), - .arena = &parse_arena.allocator, - .it = &it, - .tree = tree, - .options = options, - }; - defer parser.symbols.deinit(); - - tree.root_node = try parser.root(); - return tree; -} - -const Parser = struct { - arena: *Allocator, - it: *TokenIterator, - tree: *Tree, - - arena: *Allocator, - scopes: ScopeList, - options: Options, - - const ScopeList = std.SegmentedLists(Scope); - const SymbolList = std.SegmentedLists(Symbol); - - const Scope = struct { - kind: ScopeKind, - syms: SymbolList, - }; - - const Symbol = struct { - name: []const u8, - ty: *Type, - }; - - const ScopeKind = enum { - Block, - Loop, - Root, - Switch, - }; - - fn pushScope(parser: *Parser, kind: ScopeKind) !void { - const new = try parser.scopes.addOne(); - new.* = .{ - .kind = kind, - .syms = SymbolList.init(parser.arena), - }; - } - - fn popScope(parser: *Parser, len: usize) void { - _ = parser.scopes.pop(); - } - - fn getSymbol(parser: *Parser, tok: TokenIndex) ?*Symbol { - const name = parser.tree.tokenSlice(tok); - var scope_it = parser.scopes.iterator(parser.scopes.len); - while (scope_it.prev()) |scope| { - var sym_it = scope.syms.iterator(scope.syms.len); - while (sym_it.prev()) |sym| { - if (mem.eql(u8, sym.name, name)) { - return sym; - } - } - } - return null; - } - - fn declareSymbol(parser: *Parser, type_spec: Node.TypeSpec, dr: *Node.Declarator) Error!void { - return; // TODO - } - - /// Root <- ExternalDeclaration* eof - fn root(parser: *Parser) Allocator.Error!*Node.Root { - try parser.pushScope(.Root); - defer parser.popScope(); - const node = try parser.arena.create(Node.Root); - node.* = .{ - .decls = Node.Root.DeclList.init(parser.arena), - .eof = undefined, - }; - while (parser.externalDeclarations() catch |e| switch (e) { - error.OutOfMemory => return error.OutOfMemory, - error.ParseError => return node, - }) |decl| { - try node.decls.push(decl); - } - node.eof = parser.eatToken(.Eof) orelse return node; - return node; - } - - /// ExternalDeclaration - /// <- DeclSpec Declarator OldStyleDecl* CompoundStmt - /// / Declaration - /// OldStyleDecl <- DeclSpec Declarator (COMMA Declarator)* SEMICOLON - fn externalDeclarations(parser: *Parser) !?*Node { - return parser.declarationExtra(false); - } - - /// Declaration - /// <- DeclSpec DeclInit SEMICOLON - /// / StaticAssert - /// DeclInit <- Declarator (EQUAL Initializer)? (COMMA Declarator (EQUAL Initializer)?)* - fn declaration(parser: *Parser) !?*Node { - return parser.declarationExtra(true); - } - - fn declarationExtra(parser: *Parser, local: bool) !?*Node { - if (try parser.staticAssert()) |decl| return decl; - const begin = parser.it.index + 1; - var ds = Node.DeclSpec{}; - const got_ds = try parser.declSpec(&ds); - if (local and !got_ds) { - // not a declaration - return null; - } - switch (ds.storage_class) { - .Auto, .Register => |tok| return parser.err(.{ - .InvalidStorageClass = .{ .token = tok }, - }), - .Typedef => { - const node = try parser.arena.create(Node.Typedef); - node.* = .{ - .decl_spec = ds, - .declarators = Node.Typedef.DeclaratorList.init(parser.arena), - .semicolon = undefined, - }; - while (true) { - const dr = @fieldParentPtr(Node.Declarator, "base", (try parser.declarator(.Must)) orelse return parser.err(.{ - .ExpectedDeclarator = .{ .token = parser.it.index }, - })); - try parser.declareSymbol(ds.type_spec, dr); - try node.declarators.push(&dr.base); - if (parser.eatToken(.Comma)) |_| {} else break; - } - return &node.base; - }, - else => {}, - } - var first_dr = try parser.declarator(.Must); - if (first_dr != null and declaratorIsFunction(first_dr.?)) { - // TODO typedeffed fn proto-only - const dr = @fieldParentPtr(Node.Declarator, "base", first_dr.?); - try parser.declareSymbol(ds.type_spec, dr); - var old_decls = Node.FnDecl.OldDeclList.init(parser.arena); - const body = if (parser.eatToken(.Semicolon)) |_| - null - else blk: { - if (local) { - // TODO nested function warning - } - // TODO first_dr.is_old - // while (true) { - // var old_ds = Node.DeclSpec{}; - // if (!(try parser.declSpec(&old_ds))) { - // // not old decl - // break; - // } - // var old_dr = (try parser.declarator(.Must)); - // // if (old_dr == null) - // // try parser.err(.{ - // // .NoParamName = .{ .token = parser.it.index }, - // // }); - // // try old_decls.push(decl); - // } - const body_node = (try parser.compoundStmt()) orelse return parser.err(.{ - .ExpectedFnBody = .{ .token = parser.it.index }, - }); - break :blk @fieldParentPtr(Node.CompoundStmt, "base", body_node); - }; - - const node = try parser.arena.create(Node.FnDecl); - node.* = .{ - .decl_spec = ds, - .declarator = dr, - .old_decls = old_decls, - .body = body, - }; - return &node.base; - } else { - switch (ds.fn_spec) { - .Inline, .Noreturn => |tok| return parser.err(.{ - .FnSpecOnNonFn = .{ .token = tok }, - }), - else => {}, - } - // TODO threadlocal without static or extern on local variable - const node = try parser.arena.create(Node.VarDecl); - node.* = .{ - .decl_spec = ds, - .initializers = Node.VarDecl.Initializers.init(parser.arena), - .semicolon = undefined, - }; - if (first_dr == null) { - node.semicolon = try parser.expectToken(.Semicolon); - const ok = switch (ds.type_spec.spec) { - .Enum => |e| e.name != null, - .Record => |r| r.name != null, - else => false, - }; - const q = ds.type_spec.qual; - if (!ok) - try parser.warn(.{ - .NothingDeclared = .{ .token = begin }, - }) - else if (q.@"const" orelse q.atomic orelse q.@"volatile" orelse q.restrict) |tok| - try parser.warn(.{ - .QualifierIgnored = .{ .token = tok }, - }); - return &node.base; - } - var dr = @fieldParentPtr(Node.Declarator, "base", first_dr.?); - while (true) { - try parser.declareSymbol(ds.type_spec, dr); - if (parser.eatToken(.Equal)) |tok| { - try node.initializers.push((try parser.initializer(dr)) orelse return parser.err(.{ - .ExpectedInitializer = .{ .token = parser.it.index }, - })); - } else try node.initializers.push(&dr.base); - if (parser.eatToken(.Comma) != null) break; - dr = @fieldParentPtr(Node.Declarator, "base", (try parser.declarator(.Must)) orelse return parser.err(.{ - .ExpectedDeclarator = .{ .token = parser.it.index }, - })); - } - node.semicolon = try parser.expectToken(.Semicolon); - return &node.base; - } - } - - fn declaratorIsFunction(node: *Node) bool { - if (node.id != .Declarator) return false; - assert(node.id == .Declarator); - const dr = @fieldParentPtr(Node.Declarator, "base", node); - if (dr.suffix != .Fn) return false; - switch (dr.prefix) { - .None, .Identifer => return true, - .Complex => |inner| { - var inner_node = inner.inner; - while (true) { - if (inner_node.id != .Declarator) return false; - assert(inner_node.id == .Declarator); - const inner_dr = @fieldParentPtr(Node.Declarator, "base", inner_node); - if (inner_dr.pointer != null) return false; - switch (inner_dr.prefix) { - .None, .Identifer => return true, - .Complex => |c| inner_node = c.inner, - } - } - }, - } - } - - /// StaticAssert <- Keyword_static_assert LPAREN ConstExpr COMMA STRINGLITERAL RPAREN SEMICOLON - fn staticAssert(parser: *Parser) !?*Node { - const tok = parser.eatToken(.Keyword_static_assert) orelse return null; - _ = try parser.expectToken(.LParen); - const const_expr = (try parser.constExpr()) orelse parser.err(.{ - .ExpectedExpr = .{ .token = parser.it.index }, - }); - _ = try parser.expectToken(.Comma); - const str = try parser.expectToken(.StringLiteral); - _ = try parser.expectToken(.RParen); - const node = try parser.arena.create(Node.StaticAssert); - node.* = .{ - .assert = tok, - .expr = const_expr, - .semicolon = try parser.expectToken(.Semicolon), - }; - return &node.base; - } - - /// DeclSpec <- (StorageClassSpec / TypeSpec / FnSpec / AlignSpec)* - /// returns true if any tokens were consumed - fn declSpec(parser: *Parser, ds: *Node.DeclSpec) !bool { - var got = false; - while ((try parser.storageClassSpec(ds)) or (try parser.typeSpec(&ds.type_spec)) or (try parser.fnSpec(ds)) or (try parser.alignSpec(ds))) { - got = true; - } - return got; - } - - /// StorageClassSpec - /// <- Keyword_typedef / Keyword_extern / Keyword_static / Keyword_thread_local / Keyword_auto / Keyword_register - fn storageClassSpec(parser: *Parser, ds: *Node.DeclSpec) !bool { - blk: { - if (parser.eatToken(.Keyword_typedef)) |tok| { - if (ds.storage_class != .None or ds.thread_local != null) - break :blk; - ds.storage_class = .{ .Typedef = tok }; - } else if (parser.eatToken(.Keyword_extern)) |tok| { - if (ds.storage_class != .None) - break :blk; - ds.storage_class = .{ .Extern = tok }; - } else if (parser.eatToken(.Keyword_static)) |tok| { - if (ds.storage_class != .None) - break :blk; - ds.storage_class = .{ .Static = tok }; - } else if (parser.eatToken(.Keyword_thread_local)) |tok| { - switch (ds.storage_class) { - .None, .Extern, .Static => {}, - else => break :blk, - } - ds.thread_local = tok; - } else if (parser.eatToken(.Keyword_auto)) |tok| { - if (ds.storage_class != .None or ds.thread_local != null) - break :blk; - ds.storage_class = .{ .Auto = tok }; - } else if (parser.eatToken(.Keyword_register)) |tok| { - if (ds.storage_class != .None or ds.thread_local != null) - break :blk; - ds.storage_class = .{ .Register = tok }; - } else return false; - return true; - } - try parser.warn(.{ - .DuplicateSpecifier = .{ .token = parser.it.index }, - }); - return true; - } - - /// TypeSpec - /// <- Keyword_void / Keyword_char / Keyword_short / Keyword_int / Keyword_long / Keyword_float / Keyword_double - /// / Keyword_signed / Keyword_unsigned / Keyword_bool / Keyword_complex / Keyword_imaginary / - /// / Keyword_atomic LPAREN TypeName RPAREN - /// / EnumSpec - /// / RecordSpec - /// / IDENTIFIER // typedef name - /// / TypeQual - fn typeSpec(parser: *Parser, type_spec: *Node.TypeSpec) !bool { - blk: { - if (parser.eatToken(.Keyword_void)) |tok| { - if (type_spec.spec != .None) - break :blk; - type_spec.spec = .{ .Void = tok }; - } else if (parser.eatToken(.Keyword_char)) |tok| { - switch (type_spec.spec) { - .None => { - type_spec.spec = .{ - .Char = .{ - .char = tok, - }, - }; - }, - .Int => |int| { - if (int.int != null) - break :blk; - type_spec.spec = .{ - .Char = .{ - .char = tok, - .sign = int.sign, - }, - }; - }, - else => break :blk, - } - } else if (parser.eatToken(.Keyword_short)) |tok| { - switch (type_spec.spec) { - .None => { - type_spec.spec = .{ - .Short = .{ - .short = tok, - }, - }; - }, - .Int => |int| { - if (int.int != null) - break :blk; - type_spec.spec = .{ - .Short = .{ - .short = tok, - .sign = int.sign, - }, - }; - }, - else => break :blk, - } - } else if (parser.eatToken(.Keyword_long)) |tok| { - switch (type_spec.spec) { - .None => { - type_spec.spec = .{ - .Long = .{ - .long = tok, - }, - }; - }, - .Int => |int| { - type_spec.spec = .{ - .Long = .{ - .long = tok, - .sign = int.sign, - .int = int.int, - }, - }; - }, - .Long => |*long| { - if (long.longlong != null) - break :blk; - long.longlong = tok; - }, - .Double => |*double| { - if (double.long != null) - break :blk; - double.long = tok; - }, - else => break :blk, - } - } else if (parser.eatToken(.Keyword_int)) |tok| { - switch (type_spec.spec) { - .None => { - type_spec.spec = .{ - .Int = .{ - .int = tok, - }, - }; - }, - .Short => |*short| { - if (short.int != null) - break :blk; - short.int = tok; - }, - .Int => |*int| { - if (int.int != null) - break :blk; - int.int = tok; - }, - .Long => |*long| { - if (long.int != null) - break :blk; - long.int = tok; - }, - else => break :blk, - } - } else if (parser.eatToken(.Keyword_signed) orelse parser.eatToken(.Keyword_unsigned)) |tok| { - switch (type_spec.spec) { - .None => { - type_spec.spec = .{ - .Int = .{ - .sign = tok, - }, - }; - }, - .Char => |*char| { - if (char.sign != null) - break :blk; - char.sign = tok; - }, - .Short => |*short| { - if (short.sign != null) - break :blk; - short.sign = tok; - }, - .Int => |*int| { - if (int.sign != null) - break :blk; - int.sign = tok; - }, - .Long => |*long| { - if (long.sign != null) - break :blk; - long.sign = tok; - }, - else => break :blk, - } - } else if (parser.eatToken(.Keyword_float)) |tok| { - if (type_spec.spec != .None) - break :blk; - type_spec.spec = .{ - .Float = .{ - .float = tok, - }, - }; - } else if (parser.eatToken(.Keyword_double)) |tok| { - if (type_spec.spec != .None) - break :blk; - type_spec.spec = .{ - .Double = .{ - .double = tok, - }, - }; - } else if (parser.eatToken(.Keyword_complex)) |tok| { - switch (type_spec.spec) { - .None => { - type_spec.spec = .{ - .Double = .{ - .complex = tok, - .double = null, - }, - }; - }, - .Float => |*float| { - if (float.complex != null) - break :blk; - float.complex = tok; - }, - .Double => |*double| { - if (double.complex != null) - break :blk; - double.complex = tok; - }, - else => break :blk, - } - } else if (parser.eatToken(.Keyword_bool)) |tok| { - if (type_spec.spec != .None) - break :blk; - type_spec.spec = .{ .Bool = tok }; - } else if (parser.eatToken(.Keyword_atomic)) |tok| { - // might be _Atomic qualifier - if (parser.eatToken(.LParen)) |_| { - if (type_spec.spec != .None) - break :blk; - const name = (try parser.typeName()) orelse return parser.err(.{ - .ExpectedTypeName = .{ .token = parser.it.index }, - }); - type_spec.spec.Atomic = .{ - .atomic = tok, - .typename = name, - .rparen = try parser.expectToken(.RParen), - }; - } else { - parser.putBackToken(tok); - } - } else if (parser.eatToken(.Keyword_enum)) |tok| { - if (type_spec.spec != .None) - break :blk; - type_spec.spec.Enum = try parser.enumSpec(tok); - } else if (parser.eatToken(.Keyword_union) orelse parser.eatToken(.Keyword_struct)) |tok| { - if (type_spec.spec != .None) - break :blk; - type_spec.spec.Record = try parser.recordSpec(tok); - } else if (parser.eatToken(.Identifier)) |tok| { - const ty = parser.getSymbol(tok) orelse { - parser.putBackToken(tok); - return false; - }; - switch (ty.id) { - .Enum => |e| blk: { - if (e.name) |some| - if (!parser.tree.tokenEql(some, tok)) - break :blk; - return parser.err(.{ - .MustUseKwToRefer = .{ .kw = e.tok, .name = tok }, - }); - }, - .Record => |r| blk: { - if (r.name) |some| - if (!parser.tree.tokenEql(some, tok)) - break :blk; - return parser.err(.{ - .MustUseKwToRefer = .{ - .kw = r.tok, - .name = tok, - }, - }); - }, - .Typedef => { - type_spec.spec = .{ - .Typedef = .{ - .sym = tok, - .sym_type = ty, - }, - }; - return true; - }, - else => {}, - } - parser.putBackToken(tok); - return false; - } - return parser.typeQual(&type_spec.qual); - } - return parser.err(.{ - .InvalidTypeSpecifier = .{ - .token = parser.it.index, - .type_spec = type_spec, - }, - }); - } - - /// TypeQual <- Keyword_const / Keyword_restrict / Keyword_volatile / Keyword_atomic - fn typeQual(parser: *Parser, qual: *Node.TypeQual) !bool { - blk: { - if (parser.eatToken(.Keyword_const)) |tok| { - if (qual.@"const" != null) - break :blk; - qual.@"const" = tok; - } else if (parser.eatToken(.Keyword_restrict)) |tok| { - if (qual.atomic != null) - break :blk; - qual.atomic = tok; - } else if (parser.eatToken(.Keyword_volatile)) |tok| { - if (qual.@"volatile" != null) - break :blk; - qual.@"volatile" = tok; - } else if (parser.eatToken(.Keyword_atomic)) |tok| { - if (qual.atomic != null) - break :blk; - qual.atomic = tok; - } else return false; - return true; - } - try parser.warn(.{ - .DuplicateQualifier = .{ .token = parser.it.index }, - }); - return true; - } - - /// FnSpec <- Keyword_inline / Keyword_noreturn - fn fnSpec(parser: *Parser, ds: *Node.DeclSpec) !bool { - blk: { - if (parser.eatToken(.Keyword_inline)) |tok| { - if (ds.fn_spec != .None) - break :blk; - ds.fn_spec = .{ .Inline = tok }; - } else if (parser.eatToken(.Keyword_noreturn)) |tok| { - if (ds.fn_spec != .None) - break :blk; - ds.fn_spec = .{ .Noreturn = tok }; - } else return false; - return true; - } - try parser.warn(.{ - .DuplicateSpecifier = .{ .token = parser.it.index }, - }); - return true; - } - - /// AlignSpec <- Keyword_alignas LPAREN (TypeName / ConstExpr) RPAREN - fn alignSpec(parser: *Parser, ds: *Node.DeclSpec) !bool { - if (parser.eatToken(.Keyword_alignas)) |tok| { - _ = try parser.expectToken(.LParen); - const node = (try parser.typeName()) orelse (try parser.constExpr()) orelse parser.err(.{ - .ExpectedExpr = .{ .token = parser.it.index }, - }); - if (ds.align_spec != null) { - try parser.warn(.{ - .DuplicateSpecifier = .{ .token = parser.it.index }, - }); - } - ds.align_spec = .{ - .alignas = tok, - .expr = node, - .rparen = try parser.expectToken(.RParen), - }; - return true; - } - return false; - } - - /// EnumSpec <- Keyword_enum IDENTIFIER? (LBRACE EnumField RBRACE)? - fn enumSpec(parser: *Parser, tok: TokenIndex) !*Node.EnumType { - const node = try parser.arena.create(Node.EnumType); - const name = parser.eatToken(.Identifier); - node.* = .{ - .tok = tok, - .name = name, - .body = null, - }; - const ty = try parser.arena.create(Type); - ty.* = .{ - .id = .{ - .Enum = node, - }, - }; - if (name) |some| - try parser.symbols.append(.{ - .name = parser.tree.tokenSlice(some), - .ty = ty, - }); - if (parser.eatToken(.LBrace)) |lbrace| { - var fields = Node.EnumType.FieldList.init(parser.arena); - try fields.push((try parser.enumField()) orelse return parser.err(.{ - .ExpectedEnumField = .{ .token = parser.it.index }, - })); - while (parser.eatToken(.Comma)) |_| { - try fields.push((try parser.enumField()) orelse break); - } - node.body = .{ - .lbrace = lbrace, - .fields = fields, - .rbrace = try parser.expectToken(.RBrace), - }; - } - return node; - } - - /// EnumField <- IDENTIFIER (EQUAL ConstExpr)? (COMMA EnumField) COMMA? - fn enumField(parser: *Parser) !?*Node { - const name = parser.eatToken(.Identifier) orelse return null; - const node = try parser.arena.create(Node.EnumField); - node.* = .{ - .name = name, - .value = null, - }; - if (parser.eatToken(.Equal)) |eq| { - node.value = (try parser.constExpr()) orelse parser.err(.{ - .ExpectedExpr = .{ .token = parser.it.index }, - }); - } - return &node.base; - } - - /// RecordSpec <- (Keyword_struct / Keyword_union) IDENTIFIER? (LBRACE RecordField+ RBRACE)? - fn recordSpec(parser: *Parser, tok: TokenIndex) !*Node.RecordType { - const node = try parser.arena.create(Node.RecordType); - const name = parser.eatToken(.Identifier); - const is_struct = parser.tree.tokenSlice(tok)[0] == 's'; - node.* = .{ - .tok = tok, - .kind = if (is_struct) .Struct else .Union, - .name = name, - .body = null, - }; - const ty = try parser.arena.create(Type); - ty.* = .{ - .id = .{ - .Record = node, - }, - }; - if (name) |some| - try parser.symbols.append(.{ - .name = parser.tree.tokenSlice(some), - .ty = ty, - }); - if (parser.eatToken(.LBrace)) |lbrace| { - try parser.pushScope(.Block); - defer parser.popScope(); - var fields = Node.RecordType.FieldList.init(parser.arena); - while (true) { - if (parser.eatToken(.RBrace)) |rbrace| { - node.body = .{ - .lbrace = lbrace, - .fields = fields, - .rbrace = rbrace, - }; - break; - } - try fields.push(try parser.recordField()); - } - } - return node; - } - - /// RecordField - /// <- TypeSpec* (RecordDeclarator (COMMA RecordDeclarator))? SEMICOLON - /// \ StaticAssert - fn recordField(parser: *Parser) Error!*Node { - if (try parser.staticAssert()) |decl| return decl; - var got = false; - var type_spec = Node.TypeSpec{}; - while (try parser.typeSpec(&type_spec)) got = true; - if (!got) - return parser.err(.{ - .ExpectedType = .{ .token = parser.it.index }, - }); - const node = try parser.arena.create(Node.RecordField); - node.* = .{ - .type_spec = type_spec, - .declarators = Node.RecordField.DeclaratorList.init(parser.arena), - .semicolon = undefined, - }; - while (true) { - const rdr = try parser.recordDeclarator(); - try parser.declareSymbol(type_spec, rdr.declarator); - try node.declarators.push(&rdr.base); - if (parser.eatToken(.Comma)) |_| {} else break; - } - - node.semicolon = try parser.expectToken(.Semicolon); - return &node.base; - } - - /// TypeName <- TypeSpec* AbstractDeclarator? - fn typeName(parser: *Parser) Error!?*Node { - @panic("TODO"); - } - - /// RecordDeclarator <- Declarator? (COLON ConstExpr)? - fn recordDeclarator(parser: *Parser) Error!*Node.RecordDeclarator { - @panic("TODO"); - } - - /// Pointer <- ASTERISK TypeQual* Pointer? - fn pointer(parser: *Parser) Error!?*Node.Pointer { - const asterisk = parser.eatToken(.Asterisk) orelse return null; - const node = try parser.arena.create(Node.Pointer); - node.* = .{ - .asterisk = asterisk, - .qual = .{}, - .pointer = null, - }; - while (try parser.typeQual(&node.qual)) {} - node.pointer = try parser.pointer(); - return node; - } - - const Named = enum { - Must, - Allowed, - Forbidden, - }; - - /// Declarator <- Pointer? DeclaratorSuffix - /// DeclaratorPrefix - /// <- IDENTIFIER // if named != .Forbidden - /// / LPAREN Declarator RPAREN - /// / (none) // if named != .Must - /// DeclaratorSuffix - /// <- DeclaratorPrefix (LBRACKET ArrayDeclarator? RBRACKET)* - /// / DeclaratorPrefix LPAREN (ParamDecl (COMMA ParamDecl)* (COMMA ELLIPSIS)?)? RPAREN - fn declarator(parser: *Parser, named: Named) Error!?*Node { - const ptr = try parser.pointer(); - var node: *Node.Declarator = undefined; - var inner_fn = false; - - // TODO sizof(int (int)) - // prefix - if (parser.eatToken(.LParen)) |lparen| { - const inner = (try parser.declarator(named)) orelse return parser.err(.{ - .ExpectedDeclarator = .{ .token = lparen + 1 }, - }); - inner_fn = declaratorIsFunction(inner); - node = try parser.arena.create(Node.Declarator); - node.* = .{ - .pointer = ptr, - .prefix = .{ - .Complex = .{ - .lparen = lparen, - .inner = inner, - .rparen = try parser.expectToken(.RParen), - }, - }, - .suffix = .None, - }; - } else if (named != .Forbidden) { - if (parser.eatToken(.Identifier)) |tok| { - node = try parser.arena.create(Node.Declarator); - node.* = .{ - .pointer = ptr, - .prefix = .{ .Identifer = tok }, - .suffix = .None, - }; - } else if (named == .Must) { - return parser.err(.{ - .ExpectedToken = .{ .token = parser.it.index, .expected_id = .Identifier }, - }); - } else { - if (ptr) |some| - return &some.base; - return null; - } - } else { - node = try parser.arena.create(Node.Declarator); - node.* = .{ - .pointer = ptr, - .prefix = .None, - .suffix = .None, - }; - } - // suffix - if (parser.eatToken(.LParen)) |lparen| { - if (inner_fn) - return parser.err(.{ - .InvalidDeclarator = .{ .token = lparen }, - }); - node.suffix = .{ - .Fn = .{ - .lparen = lparen, - .params = Node.Declarator.Params.init(parser.arena), - .rparen = undefined, - }, - }; - try parser.paramDecl(node); - node.suffix.Fn.rparen = try parser.expectToken(.RParen); - } else if (parser.eatToken(.LBracket)) |tok| { - if (inner_fn) - return parser.err(.{ - .InvalidDeclarator = .{ .token = tok }, - }); - node.suffix = .{ .Array = Node.Declarator.Arrays.init(parser.arena) }; - var lbrace = tok; - while (true) { - try node.suffix.Array.push(try parser.arrayDeclarator(lbrace)); - if (parser.eatToken(.LBracket)) |t| lbrace = t else break; - } - } - if (parser.eatToken(.LParen) orelse parser.eatToken(.LBracket)) |tok| - return parser.err(.{ - .InvalidDeclarator = .{ .token = tok }, - }); - return &node.base; - } - - /// ArrayDeclarator - /// <- ASTERISK - /// / Keyword_static TypeQual* AssignmentExpr - /// / TypeQual+ (ASTERISK / Keyword_static AssignmentExpr) - /// / TypeQual+ AssignmentExpr? - /// / AssignmentExpr - fn arrayDeclarator(parser: *Parser, lbracket: TokenIndex) !*Node.Array { - const arr = try parser.arena.create(Node.Array); - arr.* = .{ - .lbracket = lbracket, - .inner = .Inferred, - .rbracket = undefined, - }; - if (parser.eatToken(.Asterisk)) |tok| { - arr.inner = .{ .Unspecified = tok }; - } else { - // TODO - } - arr.rbracket = try parser.expectToken(.RBracket); - return arr; - } - - /// Params <- ParamDecl (COMMA ParamDecl)* (COMMA ELLIPSIS)? - /// ParamDecl <- DeclSpec (Declarator / AbstractDeclarator) - fn paramDecl(parser: *Parser, dr: *Node.Declarator) !void { - var old_style = false; - while (true) { - var ds = Node.DeclSpec{}; - if (try parser.declSpec(&ds)) { - //TODO - // TODO try parser.declareSymbol(ds.type_spec, dr); - } else if (parser.eatToken(.Identifier)) |tok| { - old_style = true; - } else if (parser.eatToken(.Ellipsis)) |tok| { - // TODO - } - } - } - - /// Expr <- AssignmentExpr (COMMA Expr)* - fn expr(parser: *Parser) Error!?*Expr { - @panic("TODO"); - } - - /// AssignmentExpr - /// <- ConditionalExpr // TODO recursive? - /// / UnaryExpr (EQUAL / ASTERISKEQUAL / SLASHEQUAL / PERCENTEQUAL / PLUSEQUAL / MINUSEQUA / - /// / ANGLEBRACKETANGLEBRACKETLEFTEQUAL / ANGLEBRACKETANGLEBRACKETRIGHTEQUAL / - /// / AMPERSANDEQUAL / CARETEQUAL / PIPEEQUAL) AssignmentExpr - fn assignmentExpr(parser: *Parser) !?*Expr { - @panic("TODO"); - } - - /// ConstExpr <- ConditionalExpr - fn constExpr(parser: *Parser) Error!?*Expr { - const start = parser.it.index; - const expression = try parser.conditionalExpr(); - if (expression != null and expression.?.value == .None) - return parser.err(.{ - .ConsExpr = start, - }); - return expression; - } - - /// ConditionalExpr <- LogicalOrExpr (QUESTIONMARK Expr COLON ConditionalExpr)? - fn conditionalExpr(parser: *Parser) Error!?*Expr { - @panic("TODO"); - } - - /// LogicalOrExpr <- LogicalAndExpr (PIPEPIPE LogicalOrExpr)* - fn logicalOrExpr(parser: *Parser) !*Node { - const lhs = (try parser.logicalAndExpr()) orelse return null; - } - - /// LogicalAndExpr <- BinOrExpr (AMPERSANDAMPERSAND LogicalAndExpr)* - fn logicalAndExpr(parser: *Parser) !*Node { - @panic("TODO"); - } - - /// BinOrExpr <- BinXorExpr (PIPE BinOrExpr)* - fn binOrExpr(parser: *Parser) !*Node { - @panic("TODO"); - } - - /// BinXorExpr <- BinAndExpr (CARET BinXorExpr)* - fn binXorExpr(parser: *Parser) !*Node { - @panic("TODO"); - } - - /// BinAndExpr <- EqualityExpr (AMPERSAND BinAndExpr)* - fn binAndExpr(parser: *Parser) !*Node { - @panic("TODO"); - } - - /// EqualityExpr <- ComparisionExpr ((EQUALEQUAL / BANGEQUAL) EqualityExpr)* - fn equalityExpr(parser: *Parser) !*Node { - @panic("TODO"); - } - - /// ComparisionExpr <- ShiftExpr (ANGLEBRACKETLEFT / ANGLEBRACKETLEFTEQUAL /ANGLEBRACKETRIGHT / ANGLEBRACKETRIGHTEQUAL) ComparisionExpr)* - fn comparisionExpr(parser: *Parser) !*Node { - @panic("TODO"); - } - - /// ShiftExpr <- AdditiveExpr (ANGLEBRACKETANGLEBRACKETLEFT / ANGLEBRACKETANGLEBRACKETRIGHT) ShiftExpr)* - fn shiftExpr(parser: *Parser) !*Node { - @panic("TODO"); - } - - /// AdditiveExpr <- MultiplicativeExpr (PLUS / MINUS) AdditiveExpr)* - fn additiveExpr(parser: *Parser) !*Node { - @panic("TODO"); - } - - /// MultiplicativeExpr <- UnaryExpr (ASTERISK / SLASH / PERCENT) MultiplicativeExpr)* - fn multiplicativeExpr(parser: *Parser) !*Node { - @panic("TODO"); - } - - /// UnaryExpr - /// <- LPAREN TypeName RPAREN UnaryExpr - /// / Keyword_sizeof LAPERN TypeName RPAREN - /// / Keyword_sizeof UnaryExpr - /// / Keyword_alignof LAPERN TypeName RPAREN - /// / (AMPERSAND / ASTERISK / PLUS / PLUSPLUS / MINUS / MINUSMINUS / TILDE / BANG) UnaryExpr - /// / PrimaryExpr PostFixExpr* - fn unaryExpr(parser: *Parser) !*Node { - @panic("TODO"); - } - - /// PrimaryExpr - /// <- IDENTIFIER - /// / INTEGERLITERAL / FLOATLITERAL / STRINGLITERAL / CHARLITERAL - /// / LPAREN Expr RPAREN - /// / Keyword_generic LPAREN AssignmentExpr (COMMA Generic)+ RPAREN - fn primaryExpr(parser: *Parser) !*Node { - @panic("TODO"); - } - - /// Generic - /// <- TypeName COLON AssignmentExpr - /// / Keyword_default COLON AssignmentExpr - fn generic(parser: *Parser) !*Node { - @panic("TODO"); - } - - /// PostFixExpr - /// <- LPAREN TypeName RPAREN LBRACE Initializers RBRACE - /// / LBRACKET Expr RBRACKET - /// / LPAREN (AssignmentExpr (COMMA AssignmentExpr)*)? RPAREN - /// / (PERIOD / ARROW) IDENTIFIER - /// / (PLUSPLUS / MINUSMINUS) - fn postFixExpr(parser: *Parser) !*Node { - @panic("TODO"); - } - - /// Initializers <- ((Designator+ EQUAL)? Initializer COMMA)* (Designator+ EQUAL)? Initializer COMMA? - fn initializers(parser: *Parser) !*Node { - @panic("TODO"); - } - - /// Initializer - /// <- LBRACE Initializers RBRACE - /// / AssignmentExpr - fn initializer(parser: *Parser, dr: *Node.Declarator) Error!?*Node { - @panic("TODO"); - } - - /// Designator - /// <- LBRACKET ConstExpr RBRACKET - /// / PERIOD IDENTIFIER - fn designator(parser: *Parser) !*Node { - @panic("TODO"); - } - - /// CompoundStmt <- LBRACE (Declaration / Stmt)* RBRACE - fn compoundStmt(parser: *Parser) Error!?*Node { - const lbrace = parser.eatToken(.LBrace) orelse return null; - try parser.pushScope(.Block); - defer parser.popScope(); - const body_node = try parser.arena.create(Node.CompoundStmt); - body_node.* = .{ - .lbrace = lbrace, - .statements = Node.CompoundStmt.StmtList.init(parser.arena), - .rbrace = undefined, - }; - while (true) { - if (parser.eatToken(.RBRACE)) |rbrace| { - body_node.rbrace = rbrace; - break; - } - try body_node.statements.push((try parser.declaration()) orelse (try parser.stmt())); - } - return &body_node.base; - } - - /// Stmt - /// <- CompoundStmt - /// / Keyword_if LPAREN Expr RPAREN Stmt (Keyword_ELSE Stmt)? - /// / Keyword_switch LPAREN Expr RPAREN Stmt - /// / Keyword_while LPAREN Expr RPAREN Stmt - /// / Keyword_do statement Keyword_while LPAREN Expr RPAREN SEMICOLON - /// / Keyword_for LPAREN (Declaration / ExprStmt) ExprStmt Expr? RPAREN Stmt - /// / Keyword_default COLON Stmt - /// / Keyword_case ConstExpr COLON Stmt - /// / Keyword_goto IDENTIFIER SEMICOLON - /// / Keyword_continue SEMICOLON - /// / Keyword_break SEMICOLON - /// / Keyword_return Expr? SEMICOLON - /// / IDENTIFIER COLON Stmt - /// / ExprStmt - fn stmt(parser: *Parser) Error!*Node { - if (try parser.compoundStmt()) |node| return node; - if (parser.eatToken(.Keyword_if)) |tok| { - const node = try parser.arena.create(Node.IfStmt); - _ = try parser.expectToken(.LParen); - node.* = .{ - .@"if" = tok, - .cond = (try parser.expr()) orelse return parser.err(.{ - .ExpectedExpr = .{ .token = parser.it.index }, - }), - .body = undefined, - .@"else" = null, - }; - _ = try parser.expectToken(.RParen); - node.body = try parser.stmt(); - if (parser.eatToken(.Keyword_else)) |else_tok| { - node.@"else" = .{ - .tok = else_tok, - .body = try parser.stmt(), - }; - } - return &node.base; - } - if (parser.eatToken(.Keyword_while)) |tok| { - try parser.pushScope(.Loop); - defer parser.popScope(); - _ = try parser.expectToken(.LParen); - const cond = (try parser.expr()) orelse return parser.err(.{ - .ExpectedExpr = .{ .token = parser.it.index }, - }); - const rparen = try parser.expectToken(.RParen); - const node = try parser.arena.create(Node.WhileStmt); - node.* = .{ - .@"while" = tok, - .cond = cond, - .rparen = rparen, - .body = try parser.stmt(), - .semicolon = try parser.expectToken(.Semicolon), - }; - return &node.base; - } - if (parser.eatToken(.Keyword_do)) |tok| { - try parser.pushScope(.Loop); - defer parser.popScope(); - const body = try parser.stmt(); - _ = try parser.expectToken(.LParen); - const cond = (try parser.expr()) orelse return parser.err(.{ - .ExpectedExpr = .{ .token = parser.it.index }, - }); - _ = try parser.expectToken(.RParen); - const node = try parser.arena.create(Node.DoStmt); - node.* = .{ - .do = tok, - .body = body, - .cond = cond, - .@"while" = @"while", - .semicolon = try parser.expectToken(.Semicolon), - }; - return &node.base; - } - if (parser.eatToken(.Keyword_for)) |tok| { - try parser.pushScope(.Loop); - defer parser.popScope(); - _ = try parser.expectToken(.LParen); - const init = if (try parser.declaration()) |decl| blk: { - // TODO disallow storage class other than auto and register - break :blk decl; - } else try parser.exprStmt(); - const cond = try parser.expr(); - const semicolon = try parser.expectToken(.Semicolon); - const incr = try parser.expr(); - const rparen = try parser.expectToken(.RParen); - const node = try parser.arena.create(Node.ForStmt); - node.* = .{ - .@"for" = tok, - .init = init, - .cond = cond, - .semicolon = semicolon, - .incr = incr, - .rparen = rparen, - .body = try parser.stmt(), - }; - return &node.base; - } - if (parser.eatToken(.Keyword_switch)) |tok| { - try parser.pushScope(.Switch); - defer parser.popScope(); - _ = try parser.expectToken(.LParen); - const switch_expr = try parser.exprStmt(); - const rparen = try parser.expectToken(.RParen); - const node = try parser.arena.create(Node.SwitchStmt); - node.* = .{ - .@"switch" = tok, - .expr = switch_expr, - .rparen = rparen, - .body = try parser.stmt(), - }; - return &node.base; - } - if (parser.eatToken(.Keyword_default)) |tok| { - _ = try parser.expectToken(.Colon); - const node = try parser.arena.create(Node.LabeledStmt); - node.* = .{ - .kind = .{ .Default = tok }, - .stmt = try parser.stmt(), - }; - return &node.base; - } - if (parser.eatToken(.Keyword_case)) |tok| { - _ = try parser.expectToken(.Colon); - const node = try parser.arena.create(Node.LabeledStmt); - node.* = .{ - .kind = .{ .Case = tok }, - .stmt = try parser.stmt(), - }; - return &node.base; - } - if (parser.eatToken(.Keyword_goto)) |tok| { - const node = try parser.arena.create(Node.JumpStmt); - node.* = .{ - .ltoken = tok, - .kind = .{ .Goto = tok }, - .semicolon = try parser.expectToken(.Semicolon), - }; - return &node.base; - } - if (parser.eatToken(.Keyword_continue)) |tok| { - const node = try parser.arena.create(Node.JumpStmt); - node.* = .{ - .ltoken = tok, - .kind = .Continue, - .semicolon = try parser.expectToken(.Semicolon), - }; - return &node.base; - } - if (parser.eatToken(.Keyword_break)) |tok| { - const node = try parser.arena.create(Node.JumpStmt); - node.* = .{ - .ltoken = tok, - .kind = .Break, - .semicolon = try parser.expectToken(.Semicolon), - }; - return &node.base; - } - if (parser.eatToken(.Keyword_return)) |tok| { - const node = try parser.arena.create(Node.JumpStmt); - node.* = .{ - .ltoken = tok, - .kind = .{ .Return = try parser.expr() }, - .semicolon = try parser.expectToken(.Semicolon), - }; - return &node.base; - } - if (parser.eatToken(.Identifier)) |tok| { - if (parser.eatToken(.Colon)) |_| { - const node = try parser.arena.create(Node.LabeledStmt); - node.* = .{ - .kind = .{ .Label = tok }, - .stmt = try parser.stmt(), - }; - return &node.base; - } - parser.putBackToken(tok); - } - return parser.exprStmt(); - } - - /// ExprStmt <- Expr? SEMICOLON - fn exprStmt(parser: *Parser) !*Node { - const node = try parser.arena.create(Node.ExprStmt); - node.* = .{ - .expr = try parser.expr(), - .semicolon = try parser.expectToken(.Semicolon), - }; - return &node.base; - } - - fn eatToken(parser: *Parser, id: std.meta.Tag(Token.Id)) ?TokenIndex { - while (true) { - switch ((parser.it.next() orelse return null).id) { - .LineComment, .MultiLineComment, .Nl => continue, - else => |next_id| if (next_id == id) { - return parser.it.index; - } else { - _ = parser.it.prev(); - return null; - }, - } - } - } - - fn expectToken(parser: *Parser, id: std.meta.Tag(Token.Id)) Error!TokenIndex { - while (true) { - switch ((parser.it.next() orelse return error.ParseError).id) { - .LineComment, .MultiLineComment, .Nl => continue, - else => |next_id| if (next_id != id) { - return parser.err(.{ - .ExpectedToken = .{ .token = parser.it.index, .expected_id = id }, - }); - } else { - return parser.it.index; - }, - } - } - } - - fn putBackToken(parser: *Parser, putting_back: TokenIndex) void { - while (true) { - const prev_tok = parser.it.next() orelse return; - switch (prev_tok.id) { - .LineComment, .MultiLineComment, .Nl => continue, - else => { - assert(parser.it.list.at(putting_back) == prev_tok); - return; - }, - } - } - } - - fn err(parser: *Parser, msg: ast.Error) Error { - try parser.tree.msgs.push(.{ - .kind = .Error, - .inner = msg, - }); - return error.ParseError; - } - - fn warn(parser: *Parser, msg: ast.Error) Error!void { - const is_warning = switch (parser.options.warn_as_err) { - .None => true, - .Some => |list| for (list) |item| (if (item == msg) break false) else true, - .All => false, - }; - try parser.tree.msgs.push(.{ - .kind = if (is_warning) .Warning else .Error, - .inner = msg, - }); - if (!is_warning) return error.ParseError; - } - - fn note(parser: *Parser, msg: ast.Error) Error!void { - try parser.tree.msgs.push(.{ - .kind = .Note, - .inner = msg, - }); - } -}; diff --git a/zig/lib/std/child_process.zig b/zig/lib/std/child_process.zig index d37dd9fdf5..01db179bbe 100644 --- a/zig/lib/std/child_process.zig +++ b/zig/lib/std/child_process.zig @@ -264,7 +264,7 @@ pub const ChildProcess = struct { // TODO collect output in a deadlock-avoiding way on Windows. // https://github.com/ziglang/zig/issues/6343 - if (builtin.os.tag == .windows) { + if (builtin.os.tag == .windows or builtin.os.tag == .haiku) { const stdout_in = child.stdout.?.reader(); const stderr_in = child.stderr.?.reader(); diff --git a/zig/lib/std/compress/deflate.zig b/zig/lib/std/compress/deflate.zig index e680dc9e6f..88b9ec8672 100644 --- a/zig/lib/std/compress/deflate.zig +++ b/zig/lib/std/compress/deflate.zig @@ -384,6 +384,8 @@ pub fn InflateStream(comptime ReaderType: type) type { const last_length = lengths[i - 1]; const repeat = 3 + (try self.readBits(2)); const last_index = i + repeat; + if (last_index > lengths.len) + return error.InvalidLength; while (i < last_index) : (i += 1) { lengths[i] = last_length; } @@ -655,3 +657,17 @@ pub fn InflateStream(comptime ReaderType: type) type { pub fn inflateStream(reader: anytype, window_slice: []u8) InflateStream(@TypeOf(reader)) { return InflateStream(@TypeOf(reader)).init(reader, window_slice); } + +test "lengths overflow" { + // malformed final dynamic block, tries to write 321 code lengths (MAXCODES is 316) + // f dy hlit hdist hclen 16 17 18 0 (18) x138 (18) x138 (18) x39 (16) x6 + // 1 10 11101 11101 0000 010 010 010 010 (11) 1111111 (11) 1111111 (11) 0011100 (01) 11 + const stream = [_]u8{ 0b11101101, 0b00011101, 0b00100100, 0b11101001, 0b11111111, 0b11111111, 0b00111001, 0b00001110 }; + + const reader = std.io.fixedBufferStream(&stream).reader(); + var window: [0x8000]u8 = undefined; + var inflate = inflateStream(reader, &window); + + var buf: [1]u8 = undefined; + std.testing.expectError(error.InvalidLength, inflate.read(&buf)); +} diff --git a/zig/lib/std/crypto.zig b/zig/lib/std/crypto.zig index 457b9130d9..af23af9460 100644 --- a/zig/lib/std/crypto.zig +++ b/zig/lib/std/crypto.zig @@ -67,6 +67,7 @@ pub const dh = struct { pub const ecc = struct { pub const Curve25519 = @import("crypto/25519/curve25519.zig").Curve25519; pub const Edwards25519 = @import("crypto/25519/edwards25519.zig").Edwards25519; + pub const P256 = @import("crypto/pcurves/p256.zig").P256; pub const Ristretto255 = @import("crypto/25519/ristretto255.zig").Ristretto255; }; @@ -154,7 +155,7 @@ pub const random = &@import("crypto/tlcsprng.zig").interface; const std = @import("std.zig"); -pub const Error = @import("crypto/error.zig").Error; +pub const errors = @import("crypto/errors.zig"); test "crypto" { const please_windows_dont_oom = std.Target.current.os.tag == .windows; diff --git a/zig/lib/std/crypto/25519/curve25519.zig b/zig/lib/std/crypto/25519/curve25519.zig index d3e51ad0e0..90b0e10c4f 100644 --- a/zig/lib/std/crypto/25519/curve25519.zig +++ b/zig/lib/std/crypto/25519/curve25519.zig @@ -4,7 +4,11 @@ // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. const std = @import("std"); -const Error = std.crypto.Error; +const crypto = std.crypto; + +const IdentityElementError = crypto.errors.IdentityElementError; +const NonCanonicalError = crypto.errors.NonCanonicalError; +const WeakPublicKeyError = crypto.errors.WeakPublicKeyError; /// Group operations over Curve25519. pub const Curve25519 = struct { @@ -29,12 +33,12 @@ pub const Curve25519 = struct { pub const basePoint = Curve25519{ .x = Fe.curve25519BasePoint }; /// Check that the encoding of a Curve25519 point is canonical. - pub fn rejectNonCanonical(s: [32]u8) Error!void { + pub fn rejectNonCanonical(s: [32]u8) NonCanonicalError!void { return Fe.rejectNonCanonical(s, false); } /// Reject the neutral element. - pub fn rejectIdentity(p: Curve25519) Error!void { + pub fn rejectIdentity(p: Curve25519) IdentityElementError!void { if (p.x.isZero()) { return error.IdentityElement; } @@ -45,7 +49,7 @@ pub const Curve25519 = struct { return p.dbl().dbl().dbl(); } - fn ladder(p: Curve25519, s: [32]u8, comptime bits: usize) Error!Curve25519 { + fn ladder(p: Curve25519, s: [32]u8, comptime bits: usize) IdentityElementError!Curve25519 { var x1 = p.x; var x2 = Fe.one; var z2 = Fe.zero; @@ -86,7 +90,7 @@ pub const Curve25519 = struct { /// way to use Curve25519 for a DH operation. /// Return error.IdentityElement if the resulting point is /// the identity element. - pub fn clampedMul(p: Curve25519, s: [32]u8) Error!Curve25519 { + pub fn clampedMul(p: Curve25519, s: [32]u8) IdentityElementError!Curve25519 { var t: [32]u8 = s; scalar.clamp(&t); return try ladder(p, t, 255); @@ -96,16 +100,16 @@ pub const Curve25519 = struct { /// Return error.IdentityElement if the resulting point is /// the identity element or error.WeakPublicKey if the public /// key is a low-order point. - pub fn mul(p: Curve25519, s: [32]u8) Error!Curve25519 { + pub fn mul(p: Curve25519, s: [32]u8) (IdentityElementError || WeakPublicKeyError)!Curve25519 { const cofactor = [_]u8{8} ++ [_]u8{0} ** 31; _ = ladder(p, cofactor, 4) catch |_| return error.WeakPublicKey; return try ladder(p, s, 256); } /// Compute the Curve25519 equivalent to an Edwards25519 point. - pub fn fromEdwards25519(p: std.crypto.ecc.Edwards25519) Error!Curve25519 { + pub fn fromEdwards25519(p: crypto.ecc.Edwards25519) IdentityElementError!Curve25519 { try p.clearCofactor().rejectIdentity(); - const one = std.crypto.ecc.Edwards25519.Fe.one; + const one = crypto.ecc.Edwards25519.Fe.one; const x = one.add(p.y).mul(one.sub(p.y).invert()); // xMont=(1+yEd)/(1-yEd) return Curve25519{ .x = x }; } diff --git a/zig/lib/std/crypto/25519/ed25519.zig b/zig/lib/std/crypto/25519/ed25519.zig index e385e34f12..b48cc24b4b 100644 --- a/zig/lib/std/crypto/25519/ed25519.zig +++ b/zig/lib/std/crypto/25519/ed25519.zig @@ -8,8 +8,15 @@ const crypto = std.crypto; const debug = std.debug; const fmt = std.fmt; const mem = std.mem; + const Sha512 = crypto.hash.sha2.Sha512; -const Error = crypto.Error; + +const EncodingError = crypto.errors.EncodingError; +const IdentityElementError = crypto.errors.IdentityElementError; +const NonCanonicalError = crypto.errors.NonCanonicalError; +const SignatureVerificationError = crypto.errors.SignatureVerificationError; +const KeyMismatchError = crypto.errors.KeyMismatchError; +const WeakPublicKeyError = crypto.errors.WeakPublicKeyError; /// Ed25519 (EdDSA) signatures. pub const Ed25519 = struct { @@ -41,7 +48,7 @@ pub const Ed25519 = struct { /// /// For this reason, an EdDSA secret key is commonly called a seed, /// from which the actual secret is derived. - pub fn create(seed: ?[seed_length]u8) Error!KeyPair { + pub fn create(seed: ?[seed_length]u8) IdentityElementError!KeyPair { const ss = seed orelse ss: { var random_seed: [seed_length]u8 = undefined; crypto.random.bytes(&random_seed); @@ -51,7 +58,7 @@ pub const Ed25519 = struct { var h = Sha512.init(.{}); h.update(&ss); h.final(&az); - const p = try Curve.basePoint.clampedMul(az[0..32].*); + const p = Curve.basePoint.clampedMul(az[0..32].*) catch return error.IdentityElement; var sk: [secret_length]u8 = undefined; mem.copy(u8, &sk, &ss); const pk = p.toBytes(); @@ -72,7 +79,7 @@ pub const Ed25519 = struct { /// Sign a message using a key pair, and optional random noise. /// Having noise creates non-standard, non-deterministic signatures, /// but has been proven to increase resilience against fault attacks. - pub fn sign(msg: []const u8, key_pair: KeyPair, noise: ?[noise_length]u8) Error![signature_length]u8 { + pub fn sign(msg: []const u8, key_pair: KeyPair, noise: ?[noise_length]u8) (IdentityElementError || WeakPublicKeyError || KeyMismatchError)![signature_length]u8 { const seed = key_pair.secret_key[0..seed_length]; const public_key = key_pair.secret_key[seed_length..]; if (!mem.eql(u8, public_key, &key_pair.public_key)) { @@ -113,7 +120,7 @@ pub const Ed25519 = struct { /// Verify an Ed25519 signature given a message and a public key. /// Returns error.SignatureVerificationFailed is the signature verification failed. - pub fn verify(sig: [signature_length]u8, msg: []const u8, public_key: [public_length]u8) Error!void { + pub fn verify(sig: [signature_length]u8, msg: []const u8, public_key: [public_length]u8) (SignatureVerificationError || WeakPublicKeyError || EncodingError || NonCanonicalError || IdentityElementError)!void { const r = sig[0..32]; const s = sig[32..64]; try Curve.scalar.rejectNonCanonical(s.*); @@ -122,6 +129,7 @@ pub const Ed25519 = struct { try a.rejectIdentity(); try Curve.rejectNonCanonical(r.*); const expected_r = try Curve.fromBytes(r.*); + try expected_r.rejectIdentity(); var h = Sha512.init(.{}); h.update(r); @@ -131,8 +139,7 @@ pub const Ed25519 = struct { h.final(&hram64); const hram = Curve.scalar.reduce64(hram64); - const ah = try a.neg().mulPublic(hram); - const sb_ah = (try Curve.basePoint.mulPublic(s.*)).add(ah); + const sb_ah = try Curve.basePoint.mulDoubleBasePublic(s.*, a.neg(), hram); if (expected_r.sub(sb_ah).clearCofactor().rejectIdentity()) |_| { return error.SignatureVerificationFailed; } else |_| {} @@ -146,7 +153,7 @@ pub const Ed25519 = struct { }; /// Verify several signatures in a single operation, much faster than verifying signatures one-by-one - pub fn verifyBatch(comptime count: usize, signature_batch: [count]BatchElement) Error!void { + pub fn verifyBatch(comptime count: usize, signature_batch: [count]BatchElement) (SignatureVerificationError || IdentityElementError || WeakPublicKeyError || EncodingError || NonCanonicalError)!void { var r_batch: [count][32]u8 = undefined; var s_batch: [count][32]u8 = undefined; var a_batch: [count]Curve = undefined; @@ -161,6 +168,7 @@ pub const Ed25519 = struct { try a.rejectIdentity(); try Curve.rejectNonCanonical(r.*); const expected_r = try Curve.fromBytes(r.*); + try expected_r.rejectIdentity(); expected_r_batch[i] = expected_r; r_batch[i] = r.*; s_batch[i] = s.*; @@ -180,7 +188,7 @@ pub const Ed25519 = struct { var z_batch: [count]Curve.scalar.CompressedScalar = undefined; for (z_batch) |*z| { - std.crypto.random.bytes(z[0..16]); + crypto.random.bytes(z[0..16]); mem.set(u8, z[16..], 0); } @@ -233,8 +241,8 @@ test "ed25519 batch verification" { const key_pair = try Ed25519.KeyPair.create(null); var msg1: [32]u8 = undefined; var msg2: [32]u8 = undefined; - std.crypto.random.bytes(&msg1); - std.crypto.random.bytes(&msg2); + crypto.random.bytes(&msg1); + crypto.random.bytes(&msg2); const sig1 = try Ed25519.sign(&msg1, key_pair, null); const sig2 = try Ed25519.sign(&msg2, key_pair, null); var signature_batch = [_]Ed25519.BatchElement{ @@ -317,13 +325,13 @@ test "ed25519 test vectors" { .msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41", .public_key_hex = "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43", .sig_hex = "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03be9678ac102edcd92b0210bb34d7428d12ffc5df5f37e359941266a4e35f0f", - .expected = error.SignatureVerificationFailed, // 8 - non-canonical R + .expected = error.IdentityElement, // 8 - non-canonical R }, Vec{ .msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41", .public_key_hex = "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43", .sig_hex = "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca8c5b64cd208982aa38d4936621a4775aa233aa0505711d8fdcfdaa943d4908", - .expected = null, // 9 - non-canonical R + .expected = error.IdentityElement, // 9 - non-canonical R }, Vec{ .msg_hex = "e96b7021eb39c1a163b6da4e3093dcd3f21387da4cc4572be588fafae23c155b", diff --git a/zig/lib/std/crypto/25519/edwards25519.zig b/zig/lib/std/crypto/25519/edwards25519.zig index 89b7b9b9f3..4e3d156007 100644 --- a/zig/lib/std/crypto/25519/edwards25519.zig +++ b/zig/lib/std/crypto/25519/edwards25519.zig @@ -4,10 +4,16 @@ // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. const std = @import("std"); +const crypto = std.crypto; const debug = std.debug; const fmt = std.fmt; const mem = std.mem; -const Error = std.crypto.Error; + +const EncodingError = crypto.errors.EncodingError; +const IdentityElementError = crypto.errors.IdentityElementError; +const NonCanonicalError = crypto.errors.NonCanonicalError; +const NotSquareError = crypto.errors.NotSquareError; +const WeakPublicKeyError = crypto.errors.WeakPublicKeyError; /// Group operations over Edwards25519. pub const Edwards25519 = struct { @@ -26,7 +32,7 @@ pub const Edwards25519 = struct { is_base: bool = false, /// Decode an Edwards25519 point from its compressed (Y+sign) coordinates. - pub fn fromBytes(s: [encoded_length]u8) Error!Edwards25519 { + pub fn fromBytes(s: [encoded_length]u8) EncodingError!Edwards25519 { const z = Fe.one; const y = Fe.fromBytes(s); var u = y.sq(); @@ -56,7 +62,7 @@ pub const Edwards25519 = struct { } /// Check that the encoding of a point is canonical. - pub fn rejectNonCanonical(s: [32]u8) Error!void { + pub fn rejectNonCanonical(s: [32]u8) NonCanonicalError!void { return Fe.rejectNonCanonical(s, true); } @@ -69,19 +75,11 @@ pub const Edwards25519 = struct { .is_base = true, }; - /// The edwards25519 neutral element. - pub const neutralElement = Edwards25519{ - .x = Fe{ .limbs = .{ 2251799813685229, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247 } }, - .y = Fe{ .limbs = .{ 1507481815385608, 2223447444246085, 1083941587175919, 2059929906842505, 1581435440146976 } }, - .z = Fe{ .limbs = .{ 1507481815385608, 2223447444246085, 1083941587175919, 2059929906842505, 1581435440146976 } }, - .t = Fe{ .limbs = .{ 2251799813685229, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247 } }, - .is_base = false, - }; - - const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero }; + pub const neutralElement = @compileError("deprecated: use identityElement instead"); + pub const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero }; /// Reject the neutral element. - pub fn rejectIdentity(p: Edwards25519) Error!void { + pub fn rejectIdentity(p: Edwards25519) IdentityElementError!void { if (p.x.isZero()) { return error.IdentityElement; } @@ -154,9 +152,10 @@ pub const Edwards25519 = struct { return t; } - fn nonAdjacentForm(s: [32]u8) [2 * 32]i8 { + fn slide(s: [32]u8) [2 * 32]i8 { + const reduced = if ((s[s.len - 1] & 0x80) != 0) s else scalar.reduce(s); var e: [2 * 32]i8 = undefined; - for (s) |x, i| { + for (reduced) |x, i| { e[i * 2 + 0] = @as(i8, @truncate(u4, x)); e[i * 2 + 1] = @as(i8, @truncate(u4, x >> 4)); } @@ -177,9 +176,9 @@ pub const Edwards25519 = struct { // Based on real-world benchmarks, we only use this for multi-scalar multiplication. // NAF could be useful to half the size of precomputation tables, but we intentionally // avoid these to keep the standard library lightweight. - fn pcMul(pc: [9]Edwards25519, s: [32]u8, comptime vartime: bool) Error!Edwards25519 { + fn pcMul(pc: [9]Edwards25519, s: [32]u8, comptime vartime: bool) IdentityElementError!Edwards25519 { std.debug.assert(vartime); - const e = nonAdjacentForm(s); + const e = slide(s); var q = Edwards25519.identityElement; var pos: usize = 2 * 32 - 1; while (true) : (pos -= 1) { @@ -197,7 +196,7 @@ pub const Edwards25519 = struct { } // Scalar multiplication with a 4-bit window and the first 15 multiples. - fn pcMul16(pc: [16]Edwards25519, s: [32]u8, comptime vartime: bool) Error!Edwards25519 { + fn pcMul16(pc: [16]Edwards25519, s: [32]u8, comptime vartime: bool) IdentityElementError!Edwards25519 { var q = Edwards25519.identityElement; var pos: usize = 252; while (true) : (pos -= 4) { @@ -232,13 +231,18 @@ pub const Edwards25519 = struct { break :pc precompute(Edwards25519.basePoint, 15); }; + const basePointPc8 = comptime pc: { + @setEvalBranchQuota(10000); + break :pc precompute(Edwards25519.basePoint, 8); + }; + /// Multiply an Edwards25519 point by a scalar without clamping it. - /// Return error.WeakPublicKey if the resulting point is - /// the identity element. - pub fn mul(p: Edwards25519, s: [32]u8) Error!Edwards25519 { + /// Return error.WeakPublicKey if the base generates a small-order group, + /// and error.IdentityElement if the result is the identity element. + pub fn mul(p: Edwards25519, s: [32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 { const pc = if (p.is_base) basePointPc else pc: { const xpc = precompute(p, 15); - xpc[4].rejectIdentity() catch |_| return error.WeakPublicKey; + xpc[4].rejectIdentity() catch return error.WeakPublicKey; break :pc xpc; }; return pcMul16(pc, s, false); @@ -246,7 +250,7 @@ pub const Edwards25519 = struct { /// Multiply an Edwards25519 point by a *PUBLIC* scalar *IN VARIABLE TIME* /// This can be used for signature verification. - pub fn mulPublic(p: Edwards25519, s: [32]u8) Error!Edwards25519 { + pub fn mulPublic(p: Edwards25519, s: [32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 { if (p.is_base) { return pcMul16(basePointPc, s, true); } else { @@ -256,14 +260,50 @@ pub const Edwards25519 = struct { } } + /// Double-base multiplication of public parameters - Compute (p1*s1)+(p2*s2) *IN VARIABLE TIME* + /// This can be used for signature verification. + pub fn mulDoubleBasePublic(p1: Edwards25519, s1: [32]u8, p2: Edwards25519, s2: [32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 { + const pc1 = if (p1.is_base) basePointPc8 else pc: { + const xpc = precompute(p1, 8); + xpc[4].rejectIdentity() catch return error.WeakPublicKey; + break :pc xpc; + }; + const pc2 = if (p2.is_base) basePointPc8 else pc: { + const xpc = precompute(p2, 8); + xpc[4].rejectIdentity() catch return error.WeakPublicKey; + break :pc xpc; + }; + const e1 = slide(s1); + const e2 = slide(s2); + var q = Edwards25519.identityElement; + var pos: usize = 2 * 32 - 1; + while (true) : (pos -= 1) { + const slot1 = e1[pos]; + if (slot1 > 0) { + q = q.add(pc1[@intCast(usize, slot1)]); + } else if (slot1 < 0) { + q = q.sub(pc1[@intCast(usize, -slot1)]); + } + const slot2 = e2[pos]; + if (slot2 > 0) { + q = q.add(pc2[@intCast(usize, slot2)]); + } else if (slot2 < 0) { + q = q.sub(pc2[@intCast(usize, -slot2)]); + } + if (pos == 0) break; + q = q.dbl().dbl().dbl().dbl(); + } + try q.rejectIdentity(); + return q; + } + /// Multiscalar multiplication *IN VARIABLE TIME* for public data /// Computes ps0*ss0 + ps1*ss1 + ps2*ss2... faster than doing many of these operations individually - pub fn mulMulti(comptime count: usize, ps: [count]Edwards25519, ss: [count][32]u8) Error!Edwards25519 { + pub fn mulMulti(comptime count: usize, ps: [count]Edwards25519, ss: [count][32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 { var pcs: [count][9]Edwards25519 = undefined; for (ps) |p, i| { if (p.is_base) { - @setEvalBranchQuota(10000); - pcs[i] = comptime precompute(Edwards25519.basePoint, 8); + pcs[i] = basePointPc8; } else { pcs[i] = precompute(p, 8); pcs[i][4].rejectIdentity() catch |_| return error.WeakPublicKey; @@ -271,7 +311,7 @@ pub const Edwards25519 = struct { } var es: [count][2 * 32]i8 = undefined; for (ss) |s, i| { - es[i] = nonAdjacentForm(s); + es[i] = slide(s); } var q = Edwards25519.identityElement; var pos: usize = 2 * 32 - 1; @@ -297,14 +337,14 @@ pub const Edwards25519 = struct { /// This is strongly recommended for DH operations. /// Return error.WeakPublicKey if the resulting point is /// the identity element. - pub fn clampedMul(p: Edwards25519, s: [32]u8) Error!Edwards25519 { + pub fn clampedMul(p: Edwards25519, s: [32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 { var t: [32]u8 = s; scalar.clamp(&t); return mul(p, t); } // montgomery -- recover y = sqrt(x^3 + A*x^2 + x) - fn xmontToYmont(x: Fe) Error!Fe { + fn xmontToYmont(x: Fe) NotSquareError!Fe { var x2 = x.sq(); const x3 = x.mul(x2); x2 = x2.mul32(Fe.edwards25519a_32); @@ -367,7 +407,7 @@ pub const Edwards25519 = struct { fn stringToPoints(comptime n: usize, ctx: []const u8, s: []const u8) [n]Edwards25519 { debug.assert(n <= 2); - const H = std.crypto.hash.sha2.Sha512; + const H = crypto.hash.sha2.Sha512; const h_l: usize = 48; var xctx = ctx; var hctx: [H.digest_length]u8 = undefined; @@ -485,8 +525,8 @@ test "edwards25519 packing/unpacking" { test "edwards25519 point addition/substraction" { var s1: [32]u8 = undefined; var s2: [32]u8 = undefined; - std.crypto.random.bytes(&s1); - std.crypto.random.bytes(&s2); + crypto.random.bytes(&s1); + crypto.random.bytes(&s2); const p = try Edwards25519.basePoint.clampedMul(s1); const q = try Edwards25519.basePoint.clampedMul(s2); const r = p.add(q).add(q).sub(q).sub(q); diff --git a/zig/lib/std/crypto/25519/field.zig b/zig/lib/std/crypto/25519/field.zig index b570e2d06b..3378191451 100644 --- a/zig/lib/std/crypto/25519/field.zig +++ b/zig/lib/std/crypto/25519/field.zig @@ -4,9 +4,12 @@ // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. const std = @import("std"); +const crypto = std.crypto; const readIntLittle = std.mem.readIntLittle; const writeIntLittle = std.mem.writeIntLittle; -const Error = std.crypto.Error; + +const NonCanonicalError = crypto.errors.NonCanonicalError; +const NotSquareError = crypto.errors.NotSquareError; pub const Fe = struct { limbs: [5]u64, @@ -113,7 +116,7 @@ pub const Fe = struct { } /// Reject non-canonical encodings of an element, possibly ignoring the top bit - pub fn rejectNonCanonical(s: [32]u8, comptime ignore_extra_bit: bool) Error!void { + pub fn rejectNonCanonical(s: [32]u8, comptime ignore_extra_bit: bool) NonCanonicalError!void { var c: u16 = (s[31] & 0x7f) ^ 0x7f; comptime var i = 30; inline while (i > 0) : (i -= 1) { @@ -289,7 +292,7 @@ pub const Fe = struct { return _carry128(&r); } - fn _sq(a: Fe, double: comptime bool) callconv(.Inline) Fe { + fn _sq(a: Fe, comptime double: bool) callconv(.Inline) Fe { var ax: [5]u128 = undefined; var r: [5]u128 = undefined; comptime var i = 0; @@ -352,7 +355,7 @@ pub const Fe = struct { return fe; } - /// Compute the inverse of a field element + /// Return the inverse of a field element, or 0 if a=0. pub fn invert(a: Fe) Fe { var t0 = a.sq(); var t1 = t0.sqn(2).mul(a); @@ -413,7 +416,7 @@ pub const Fe = struct { } /// Compute the square root of `x2`, returning `error.NotSquare` if `x2` was not a square - pub fn sqrt(x2: Fe) Error!Fe { + pub fn sqrt(x2: Fe) NotSquareError!Fe { var x2_copy = x2; const x = x2.uncheckedSqrt(); const check = x.sq().sub(x2_copy); diff --git a/zig/lib/std/crypto/25519/ristretto255.zig b/zig/lib/std/crypto/25519/ristretto255.zig index 4644b7622e..50f1580a80 100644 --- a/zig/lib/std/crypto/25519/ristretto255.zig +++ b/zig/lib/std/crypto/25519/ristretto255.zig @@ -5,7 +5,11 @@ // and substantial portions of the software. const std = @import("std"); const fmt = std.fmt; -const Error = std.crypto.Error; + +const EncodingError = std.crypto.errors.EncodingError; +const IdentityElementError = std.crypto.errors.IdentityElementError; +const NonCanonicalError = std.crypto.errors.NonCanonicalError; +const WeakPublicKeyError = std.crypto.errors.WeakPublicKeyError; /// Group operations over Edwards25519. pub const Ristretto255 = struct { @@ -35,7 +39,7 @@ pub const Ristretto255 = struct { return .{ .ratio_is_square = @boolToInt(has_m_root) | @boolToInt(has_p_root), .root = x.abs() }; } - fn rejectNonCanonical(s: [encoded_length]u8) Error!void { + fn rejectNonCanonical(s: [encoded_length]u8) NonCanonicalError!void { if ((s[0] & 1) != 0) { return error.NonCanonical; } @@ -43,7 +47,7 @@ pub const Ristretto255 = struct { } /// Reject the neutral element. - pub fn rejectIdentity(p: Ristretto255) callconv(.Inline) Error!void { + pub fn rejectIdentity(p: Ristretto255) callconv(.Inline) IdentityElementError!void { return p.p.rejectIdentity(); } @@ -51,7 +55,7 @@ pub const Ristretto255 = struct { pub const basePoint = Ristretto255{ .p = Curve.basePoint }; /// Decode a Ristretto255 representative. - pub fn fromBytes(s: [encoded_length]u8) Error!Ristretto255 { + pub fn fromBytes(s: [encoded_length]u8) (NonCanonicalError || EncodingError)!Ristretto255 { try rejectNonCanonical(s); const s_ = Fe.fromBytes(s); const ss = s_.sq(); // s^2 @@ -154,7 +158,7 @@ pub const Ristretto255 = struct { /// Multiply a Ristretto255 element with a scalar. /// Return error.WeakPublicKey if the resulting element is /// the identity element. - pub fn mul(p: Ristretto255, s: [encoded_length]u8) callconv(.Inline) Error!Ristretto255 { + pub fn mul(p: Ristretto255, s: [encoded_length]u8) callconv(.Inline) (IdentityElementError || WeakPublicKeyError)!Ristretto255 { return Ristretto255{ .p = try p.p.mul(s) }; } diff --git a/zig/lib/std/crypto/25519/scalar.zig b/zig/lib/std/crypto/25519/scalar.zig index a4bf5aafcf..e8564ff577 100644 --- a/zig/lib/std/crypto/25519/scalar.zig +++ b/zig/lib/std/crypto/25519/scalar.zig @@ -5,7 +5,8 @@ // and substantial portions of the software. const std = @import("std"); const mem = std.mem; -const Error = std.crypto.Error; + +const NonCanonicalError = std.crypto.errors.NonCanonicalError; /// 2^252 + 27742317777372353535851937790883648493 pub const field_size = [32]u8{ @@ -19,7 +20,7 @@ pub const CompressedScalar = [32]u8; pub const zero = [_]u8{0} ** 32; /// Reject a scalar whose encoding is not canonical. -pub fn rejectNonCanonical(s: [32]u8) Error!void { +pub fn rejectNonCanonical(s: [32]u8) NonCanonicalError!void { var c: u8 = 0; var n: u8 = 1; var i: usize = 31; @@ -97,7 +98,7 @@ pub fn sub(a: [32]u8, b: [32]u8) [32]u8 { return add(a, neg(b)); } -/// A scalar in unpacked reprentation +/// A scalar in unpacked representation pub const Scalar = struct { const Limbs = [5]u64; limbs: Limbs = undefined, diff --git a/zig/lib/std/crypto/25519/x25519.zig b/zig/lib/std/crypto/25519/x25519.zig index 2d53124056..07b1dc7a86 100644 --- a/zig/lib/std/crypto/25519/x25519.zig +++ b/zig/lib/std/crypto/25519/x25519.zig @@ -9,7 +9,10 @@ const mem = std.mem; const fmt = std.fmt; const Sha512 = crypto.hash.sha2.Sha512; -const Error = crypto.Error; + +const EncodingError = crypto.errors.EncodingError; +const IdentityElementError = crypto.errors.IdentityElementError; +const WeakPublicKeyError = crypto.errors.WeakPublicKeyError; /// X25519 DH function. pub const X25519 = struct { @@ -32,7 +35,7 @@ pub const X25519 = struct { secret_key: [secret_length]u8, /// Create a new key pair using an optional seed. - pub fn create(seed: ?[seed_length]u8) Error!KeyPair { + pub fn create(seed: ?[seed_length]u8) IdentityElementError!KeyPair { const sk = seed orelse sk: { var random_seed: [seed_length]u8 = undefined; crypto.random.bytes(&random_seed); @@ -45,7 +48,7 @@ pub const X25519 = struct { } /// Create a key pair from an Ed25519 key pair - pub fn fromEd25519(ed25519_key_pair: crypto.sign.Ed25519.KeyPair) Error!KeyPair { + pub fn fromEd25519(ed25519_key_pair: crypto.sign.Ed25519.KeyPair) (IdentityElementError || EncodingError)!KeyPair { const seed = ed25519_key_pair.secret_key[0..32]; var az: [Sha512.digest_length]u8 = undefined; Sha512.hash(seed, &az, .{}); @@ -60,13 +63,13 @@ pub const X25519 = struct { }; /// Compute the public key for a given private key. - pub fn recoverPublicKey(secret_key: [secret_length]u8) Error![public_length]u8 { + pub fn recoverPublicKey(secret_key: [secret_length]u8) IdentityElementError![public_length]u8 { const q = try Curve.basePoint.clampedMul(secret_key); return q.toBytes(); } /// Compute the X25519 equivalent to an Ed25519 public eky. - pub fn publicKeyFromEd25519(ed25519_public_key: [crypto.sign.Ed25519.public_length]u8) Error![public_length]u8 { + pub fn publicKeyFromEd25519(ed25519_public_key: [crypto.sign.Ed25519.public_length]u8) (IdentityElementError || EncodingError)![public_length]u8 { const pk_ed = try crypto.ecc.Edwards25519.fromBytes(ed25519_public_key); const pk = try Curve.fromEdwards25519(pk_ed); return pk.toBytes(); @@ -75,7 +78,7 @@ pub const X25519 = struct { /// Compute the scalar product of a public key and a secret scalar. /// Note that the output should not be used as a shared secret without /// hashing it first. - pub fn scalarmult(secret_key: [secret_length]u8, public_key: [public_length]u8) Error![shared_length]u8 { + pub fn scalarmult(secret_key: [secret_length]u8, public_key: [public_length]u8) IdentityElementError![shared_length]u8 { const q = try Curve.fromBytes(public_key).clampedMul(secret_key); return q.toBytes(); } diff --git a/zig/lib/std/crypto/aegis.zig b/zig/lib/std/crypto/aegis.zig index 3969d59e10..59dcf04dac 100644 --- a/zig/lib/std/crypto/aegis.zig +++ b/zig/lib/std/crypto/aegis.zig @@ -8,7 +8,7 @@ const std = @import("std"); const mem = std.mem; const assert = std.debug.assert; const AesBlock = std.crypto.core.aes.Block; -const Error = std.crypto.Error; +const AuthenticationError = std.crypto.errors.AuthenticationError; const State128L = struct { blocks: [8]AesBlock, @@ -137,7 +137,7 @@ pub const Aegis128L = struct { /// ad: Associated Data /// npub: public nonce /// k: private key - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void { assert(c.len == m.len); var state = State128L.init(key, npub); var src: [32]u8 align(16) = undefined; @@ -299,7 +299,7 @@ pub const Aegis256 = struct { /// ad: Associated Data /// npub: public nonce /// k: private key - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void { assert(c.len == m.len); var state = State256.init(key, npub); var src: [16]u8 align(16) = undefined; diff --git a/zig/lib/std/crypto/aes_gcm.zig b/zig/lib/std/crypto/aes_gcm.zig index bcb1b4c5fa..70746af073 100644 --- a/zig/lib/std/crypto/aes_gcm.zig +++ b/zig/lib/std/crypto/aes_gcm.zig @@ -12,7 +12,7 @@ const debug = std.debug; const Ghash = std.crypto.onetimeauth.Ghash; const mem = std.mem; const modes = crypto.core.modes; -const Error = crypto.Error; +const AuthenticationError = crypto.errors.AuthenticationError; pub const Aes128Gcm = AesGcm(crypto.core.aes.Aes128); pub const Aes256Gcm = AesGcm(crypto.core.aes.Aes256); @@ -60,7 +60,7 @@ fn AesGcm(comptime Aes: anytype) type { } } - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void { assert(c.len == m.len); const aes = Aes.initEnc(key); diff --git a/zig/lib/std/crypto/aes_ocb.zig b/zig/lib/std/crypto/aes_ocb.zig index 9eb0561d9f..658b3b97ce 100644 --- a/zig/lib/std/crypto/aes_ocb.zig +++ b/zig/lib/std/crypto/aes_ocb.zig @@ -10,7 +10,7 @@ const aes = crypto.core.aes; const assert = std.debug.assert; const math = std.math; const mem = std.mem; -const Error = crypto.Error; +const AuthenticationError = crypto.errors.AuthenticationError; pub const Aes128Ocb = AesOcb(aes.Aes128); pub const Aes256Ocb = AesOcb(aes.Aes256); @@ -179,7 +179,7 @@ fn AesOcb(comptime Aes: anytype) type { /// ad: Associated Data /// npub: public nonce /// k: secret key - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void { assert(c.len == m.len); const aes_enc_ctx = Aes.initEnc(key); diff --git a/zig/lib/std/crypto/bcrypt.zig b/zig/lib/std/crypto/bcrypt.zig index d00108b9c4..51fb144b2f 100644 --- a/zig/lib/std/crypto/bcrypt.zig +++ b/zig/lib/std/crypto/bcrypt.zig @@ -12,7 +12,8 @@ const mem = std.mem; const debug = std.debug; const testing = std.testing; const utils = crypto.utils; -const Error = crypto.Error; +const EncodingError = crypto.errors.EncodingError; +const PasswordVerificationError = crypto.errors.PasswordVerificationError; const salt_length: usize = 16; const salt_str_length: usize = 22; @@ -179,7 +180,7 @@ const Codec = struct { debug.assert(j == b64.len); } - fn decode(bin: []u8, b64: []const u8) Error!void { + fn decode(bin: []u8, b64: []const u8) EncodingError!void { var i: usize = 0; var j: usize = 0; while (j < bin.len) { @@ -204,7 +205,7 @@ const Codec = struct { } }; -fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8) Error![hash_length]u8 { +fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8) ![hash_length]u8 { var state = State{}; var password_buf: [73]u8 = undefined; const trimmed_len = math.min(password.len, password_buf.len - 1); @@ -252,14 +253,14 @@ fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8) /// IMPORTANT: by design, bcrypt silently truncates passwords to 72 bytes. /// If this is an issue for your application, hash the password first using a function such as SHA-512, /// and then use the resulting hash as the password parameter for bcrypt. -pub fn strHash(password: []const u8, rounds_log: u6) Error![hash_length]u8 { +pub fn strHash(password: []const u8, rounds_log: u6) ![hash_length]u8 { var salt: [salt_length]u8 = undefined; crypto.random.bytes(&salt); return strHashInternal(password, rounds_log, salt); } /// Verify that a previously computed hash is valid for a given password. -pub fn strVerify(h: [hash_length]u8, password: []const u8) Error!void { +pub fn strVerify(h: [hash_length]u8, password: []const u8) (EncodingError || PasswordVerificationError)!void { if (!mem.eql(u8, "$2", h[0..2])) return error.InvalidEncoding; if (h[3] != '$' or h[6] != '$') return error.InvalidEncoding; const rounds_log_str = h[4..][0..2]; diff --git a/zig/lib/std/crypto/chacha20.zig b/zig/lib/std/crypto/chacha20.zig index e1fe3e232d..ea9eafd356 100644 --- a/zig/lib/std/crypto/chacha20.zig +++ b/zig/lib/std/crypto/chacha20.zig @@ -13,7 +13,7 @@ const testing = std.testing; const maxInt = math.maxInt; const Vector = std.meta.Vector; const Poly1305 = std.crypto.onetimeauth.Poly1305; -const Error = std.crypto.Error; +const AuthenticationError = std.crypto.errors.AuthenticationError; /// IETF-variant of the ChaCha20 stream cipher, as designed for TLS. pub const ChaCha20IETF = ChaChaIETF(20); @@ -521,7 +521,7 @@ fn ChaChaPoly1305(comptime rounds_nb: usize) type { /// npub: public nonce /// k: private key /// NOTE: the check of the authentication tag is currently not done in constant time - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void { assert(c.len == m.len); var polyKey = [_]u8{0} ** 32; @@ -583,7 +583,7 @@ fn XChaChaPoly1305(comptime rounds_nb: usize) type { /// ad: Associated Data /// npub: public nonce /// k: private key - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void { const extended = extend(k, npub, rounds_nb); return ChaChaPoly1305(rounds_nb).decrypt(m, c, tag, ad, extended.nonce, extended.key); } diff --git a/zig/lib/std/crypto/error.zig b/zig/lib/std/crypto/error.zig deleted file mode 100644 index 4cb12bb8f7..0000000000 --- a/zig/lib/std/crypto/error.zig +++ /dev/null @@ -1,34 +0,0 @@ -pub const Error = error{ - /// MAC verification failed - The tag doesn't verify for the given ciphertext and secret key - AuthenticationFailed, - - /// The requested output length is too long for the chosen algorithm - OutputTooLong, - - /// Finite field operation returned the identity element - IdentityElement, - - /// Encoded input cannot be decoded - InvalidEncoding, - - /// The signature does't verify for the given message and public key - SignatureVerificationFailed, - - /// Both a public and secret key have been provided, but they are incompatible - KeyMismatch, - - /// Encoded input is not in canonical form - NonCanonical, - - /// Square root has no solutions - NotSquare, - - /// Verification string doesn't match the provided password and parameters - PasswordVerificationFailed, - - /// Parameters would be insecure to use - WeakParameters, - - /// Public key would be insecure to use - WeakPublicKey, -}; diff --git a/zig/lib/std/crypto/errors.zig b/zig/lib/std/crypto/errors.zig new file mode 100644 index 0000000000..4d79055919 --- /dev/null +++ b/zig/lib/std/crypto/errors.zig @@ -0,0 +1,35 @@ +/// MAC verification failed - The tag doesn't verify for the given ciphertext and secret key +pub const AuthenticationError = error{AuthenticationFailed}; + +/// The requested output length is too long for the chosen algorithm +pub const OutputTooLongError = error{OutputTooLong}; + +/// Finite field operation returned the identity element +pub const IdentityElementError = error{IdentityElement}; + +/// Encoded input cannot be decoded +pub const EncodingError = error{InvalidEncoding}; + +/// The signature does't verify for the given message and public key +pub const SignatureVerificationError = error{SignatureVerificationFailed}; + +/// Both a public and secret key have been provided, but they are incompatible +pub const KeyMismatchError = error{KeyMismatch}; + +/// Encoded input is not in canonical form +pub const NonCanonicalError = error{NonCanonical}; + +/// Square root has no solutions +pub const NotSquareError = error{NotSquare}; + +/// Verification string doesn't match the provided password and parameters +pub const PasswordVerificationError = error{PasswordVerificationFailed}; + +/// Parameters would be insecure to use +pub const WeakParametersError = error{WeakParameters}; + +/// Public key would be insecure to use +pub const WeakPublicKeyError = error{WeakPublicKey}; + +/// Any error related to cryptography operations +pub const Error = AuthenticationError || OutputTooLongError || IdentityElementError || EncodingError || SignatureVerificationError || KeyMismatchError || NonCanonicalError || NotSquareError || PasswordVerificationError || WeakParametersError || WeakPublicKeyError; diff --git a/zig/lib/std/crypto/gimli.zig b/zig/lib/std/crypto/gimli.zig index 111e0c5274..fb67c25343 100644 --- a/zig/lib/std/crypto/gimli.zig +++ b/zig/lib/std/crypto/gimli.zig @@ -20,7 +20,7 @@ const assert = std.debug.assert; const testing = std.testing; const htest = @import("test.zig"); const Vector = std.meta.Vector; -const Error = std.crypto.Error; +const AuthenticationError = std.crypto.errors.AuthenticationError; pub const State = struct { pub const BLOCKBYTES = 48; @@ -393,7 +393,7 @@ pub const Aead = struct { /// npub: public nonce /// k: private key /// NOTE: the check of the authentication tag is currently not done in constant time - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void { assert(c.len == m.len); var state = Aead.init(ad, npub, k); diff --git a/zig/lib/std/crypto/isap.zig b/zig/lib/std/crypto/isap.zig index 5219742d85..6deb4977bb 100644 --- a/zig/lib/std/crypto/isap.zig +++ b/zig/lib/std/crypto/isap.zig @@ -3,7 +3,7 @@ const debug = std.debug; const mem = std.mem; const math = std.math; const testing = std.testing; -const Error = std.crypto.Error; +const AuthenticationError = std.crypto.errors.AuthenticationError; /// ISAPv2 is an authenticated encryption system hardened against side channels and fault attacks. /// https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/round-2/spec-doc-rnd2/isap-spec-round2.pdf @@ -218,7 +218,7 @@ pub const IsapA128A = struct { tag.* = mac(c, ad, npub, key); } - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void { var computed_tag = mac(c, ad, npub, key); var acc: u8 = 0; for (computed_tag) |_, j| { diff --git a/zig/lib/std/crypto/pbkdf2.zig b/zig/lib/std/crypto/pbkdf2.zig index 575fb83006..f93a5235af 100644 --- a/zig/lib/std/crypto/pbkdf2.zig +++ b/zig/lib/std/crypto/pbkdf2.zig @@ -7,7 +7,8 @@ const std = @import("std"); const mem = std.mem; const maxInt = std.math.maxInt; -const Error = std.crypto.Error; +const OutputTooLongError = std.crypto.errors.OutputTooLongError; +const WeakParametersError = std.crypto.errors.WeakParametersError; // RFC 2898 Section 5.2 // @@ -55,7 +56,7 @@ const Error = std.crypto.Error; /// the dk. It is common to tune this parameter to achieve approximately 100ms. /// /// Prf: Pseudo-random function to use. A common choice is `std.crypto.auth.hmac.HmacSha256`. -pub fn pbkdf2(dk: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Prf: type) Error!void { +pub fn pbkdf2(dk: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Prf: type) (WeakParametersError || OutputTooLongError)!void { if (rounds < 1) return error.WeakParameters; const dk_len = dk.len; diff --git a/zig/lib/std/crypto/pcurves/common.zig b/zig/lib/std/crypto/pcurves/common.zig new file mode 100644 index 0000000000..5a979fe3e5 --- /dev/null +++ b/zig/lib/std/crypto/pcurves/common.zig @@ -0,0 +1,284 @@ +const std = @import("std"); +const builtin = std.builtin; +const crypto = std.crypto; +const debug = std.debug; +const mem = std.mem; +const meta = std.meta; + +const NonCanonicalError = crypto.errors.NonCanonicalError; +const NotSquareError = crypto.errors.NotSquareError; + +/// Parameters to create a finite field type. +pub const FieldParams = struct { + fiat: type, + field_order: comptime_int, + field_bits: comptime_int, + saturated_bits: comptime_int, + encoded_length: comptime_int, +}; + +/// A field element, internally stored in Montgomery domain. +pub fn Field(comptime params: FieldParams) type { + const fiat = params.fiat; + const Limbs = fiat.Limbs; + + return struct { + const Fe = @This(); + + limbs: Limbs, + + /// Field size. + pub const field_order = params.field_order; + + /// Number of bits to represent the set of all elements. + pub const field_bits = params.field_bits; + + /// Number of bits that can be saturated without overflowing. + pub const saturated_bits = params.saturated_bits; + + /// Number of bytes required to encode an element. + pub const encoded_length = params.encoded_length; + + /// Zero. + pub const zero: Fe = Fe{ .limbs = mem.zeroes(Limbs) }; + + /// One. + pub const one = comptime one: { + var fe: Fe = undefined; + fiat.setOne(&fe.limbs); + break :one fe; + }; + + /// Reject non-canonical encodings of an element. + pub fn rejectNonCanonical(s_: [encoded_length]u8, endian: builtin.Endian) NonCanonicalError!void { + var s = if (endian == .Little) s_ else orderSwap(s_); + const field_order_s = comptime fos: { + var fos: [encoded_length]u8 = undefined; + mem.writeIntLittle(std.meta.Int(.unsigned, encoded_length * 8), &fos, field_order); + break :fos fos; + }; + if (crypto.utils.timingSafeCompare(u8, &s, &field_order_s, .Little) != .lt) { + return error.NonCanonical; + } + } + + /// Swap the endianness of an encoded element. + pub fn orderSwap(s: [encoded_length]u8) [encoded_length]u8 { + var t = s; + for (s) |x, i| t[t.len - 1 - i] = x; + return t; + } + + /// Unpack a field element. + pub fn fromBytes(s_: [encoded_length]u8, endian: builtin.Endian) NonCanonicalError!Fe { + var s = if (endian == .Little) s_ else orderSwap(s_); + try rejectNonCanonical(s, .Little); + var limbs_z: Limbs = undefined; + fiat.fromBytes(&limbs_z, s); + var limbs: Limbs = undefined; + fiat.toMontgomery(&limbs, limbs_z); + return Fe{ .limbs = limbs }; + } + + /// Pack a field element. + pub fn toBytes(fe: Fe, endian: builtin.Endian) [encoded_length]u8 { + var limbs_z: Limbs = undefined; + fiat.fromMontgomery(&limbs_z, fe.limbs); + var s: [encoded_length]u8 = undefined; + fiat.toBytes(&s, limbs_z); + return if (endian == .Little) s else orderSwap(s); + } + + /// Element as an integer. + pub const IntRepr = meta.Int(.unsigned, params.field_bits); + + /// Create a field element from an integer. + pub fn fromInt(comptime x: IntRepr) NonCanonicalError!Fe { + var s: [encoded_length]u8 = undefined; + mem.writeIntLittle(IntRepr, &s, x); + return fromBytes(s, .Little); + } + + /// Return the field element as an integer. + pub fn toInt(fe: Fe) IntRepr { + const s = fe.toBytes(.Little); + return mem.readIntLittle(IntRepr, &s); + } + + /// Return true if the field element is zero. + pub fn isZero(fe: Fe) bool { + var z: @TypeOf(fe.limbs[0]) = undefined; + fiat.nonzero(&z, fe.limbs); + return z == 0; + } + + /// Return true if both field elements are equivalent. + pub fn equivalent(a: Fe, b: Fe) bool { + return a.sub(b).isZero(); + } + + /// Return true if the element is odd. + pub fn isOdd(fe: Fe) bool { + const s = fe.toBytes(.Little); + return @truncate(u1, s[0]) != 0; + } + + /// Conditonally replace a field element with `a` if `c` is positive. + pub fn cMov(fe: *Fe, a: Fe, c: u1) void { + fiat.selectznz(&fe.limbs, c, fe.limbs, a.limbs); + } + + /// Add field elements. + pub fn add(a: Fe, b: Fe) Fe { + var fe: Fe = undefined; + fiat.add(&fe.limbs, a.limbs, b.limbs); + return fe; + } + + /// Subtract field elements. + pub fn sub(a: Fe, b: Fe) Fe { + var fe: Fe = undefined; + fiat.sub(&fe.limbs, a.limbs, b.limbs); + return fe; + } + + /// Double a field element. + pub fn dbl(a: Fe) Fe { + var fe: Fe = undefined; + fiat.add(&fe.limbs, a.limbs, a.limbs); + return fe; + } + + /// Multiply field elements. + pub fn mul(a: Fe, b: Fe) Fe { + var fe: Fe = undefined; + fiat.mul(&fe.limbs, a.limbs, b.limbs); + return fe; + } + + /// Square a field element. + pub fn sq(a: Fe) Fe { + var fe: Fe = undefined; + fiat.square(&fe.limbs, a.limbs); + return fe; + } + + /// Square a field element n times. + fn sqn(a: Fe, comptime n: comptime_int) Fe { + var i: usize = 0; + var fe = a; + while (i < n) : (i += 1) { + fe = fe.sq(); + } + return fe; + } + + /// Compute a^n. + pub fn pow(a: Fe, comptime T: type, comptime n: T) Fe { + var fe = one; + var x: T = n; + var t = a; + while (true) { + if (@truncate(u1, x) != 0) fe = fe.mul(t); + x >>= 1; + if (x == 0) break; + t = t.sq(); + } + return fe; + } + + /// Negate a field element. + pub fn neg(a: Fe) Fe { + var fe: Fe = undefined; + fiat.opp(&fe.limbs, a.limbs); + return fe; + } + + /// Return the inverse of a field element, or 0 if a=0. + // Field inversion from https://eprint.iacr.org/2021/549.pdf + pub fn invert(a: Fe) Fe { + const iterations = (49 * field_bits + 57) / 17; + const Word = @TypeOf(a.limbs[0]); + const XLimbs = [a.limbs.len + 1]Word; + + var d: Word = 1; + var f: XLimbs = undefined; + fiat.msat(&f); + + var g: XLimbs = undefined; + fiat.fromMontgomery(g[0..a.limbs.len], a.limbs); + g[g.len - 1] = 0; + + var r: Limbs = undefined; + fiat.setOne(&r); + var v = mem.zeroes(Limbs); + + var precomp: Limbs = undefined; + fiat.divstepPrecomp(&precomp); + + var out1: Word = undefined; + var out2: XLimbs = undefined; + var out3: XLimbs = undefined; + var out4: Limbs = undefined; + var out5: Limbs = undefined; + + var i: usize = 0; + while (i < iterations - iterations % 2) : (i += 2) { + fiat.divstep(&out1, &out2, &out3, &out4, &out5, d, f, g, v, r); + fiat.divstep(&d, &f, &g, &v, &r, out1, out2, out3, out4, out5); + } + if (iterations % 2 != 0) { + fiat.divstep(&out1, &out2, &out3, &out4, &out5, d, f, g, v, r); + mem.copy(Word, &v, &out4); + mem.copy(Word, &f, &out2); + } + var v_opp: Limbs = undefined; + fiat.opp(&v_opp, v); + fiat.selectznz(&v, @truncate(u1, f[f.len - 1] >> (meta.bitCount(Word) - 1)), v, v_opp); + var fe: Fe = undefined; + fiat.mul(&fe.limbs, v, precomp); + return fe; + } + + /// Return true if the field element is a square. + pub fn isSquare(x2: Fe) bool { + if (field_order == 115792089210356248762697446949407573530086143415290314195533631308867097853951) { + const t110 = x2.mul(x2.sq()).sq(); + const t111 = x2.mul(t110); + const t111111 = t111.mul(x2.mul(t110).sqn(3)); + const x15 = t111111.sqn(6).mul(t111111).sqn(3).mul(t111); + const x16 = x15.sq().mul(x2); + const x53 = x16.sqn(16).mul(x16).sqn(15); + const x47 = x15.mul(x53); + const ls = x47.mul(((x53.sqn(17).mul(x2)).sqn(143).mul(x47)).sqn(47)).sq().mul(x2); + return ls.equivalent(Fe.one); + } else { + const ls = x2.pow(std.meta.Int(.unsigned, field_bits), (field_order - 1) / 2); // Legendre symbol + return ls.equivalent(Fe.one); + } + } + + // x=x2^((field_order+1)/4) w/ field order=3 (mod 4). + fn uncheckedSqrt(x2: Fe) Fe { + comptime debug.assert(field_order % 4 == 3); + if (field_order == 115792089210356248762697446949407573530086143415290314195533631308867097853951) { + const t11 = x2.mul(x2.sq()); + const t1111 = t11.mul(t11.sqn(2)); + const t11111111 = t1111.mul(t1111.sqn(4)); + const x16 = t11111111.sqn(8).mul(t11111111); + return x16.sqn(16).mul(x16).sqn(32).mul(x2).sqn(96).mul(x2).sqn(94); + } else { + return x2.pow(std.meta.Int(.unsigned, field_bits), (field_order + 1) / 4); + } + } + + /// Compute the square root of `x2`, returning `error.NotSquare` if `x2` was not a square. + pub fn sqrt(x2: Fe) NotSquareError!Fe { + const x = x2.uncheckedSqrt(); + if (x.sq().equivalent(x2)) { + return x; + } + return error.NotSquare; + } + }; +} diff --git a/zig/lib/std/crypto/pcurves/p256.zig b/zig/lib/std/crypto/pcurves/p256.zig new file mode 100644 index 0000000000..0b1c584fa9 --- /dev/null +++ b/zig/lib/std/crypto/pcurves/p256.zig @@ -0,0 +1,412 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2021 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. + +const std = @import("std"); +const builtin = std.builtin; +const crypto = std.crypto; +const mem = std.mem; +const meta = std.meta; + +const EncodingError = crypto.errors.EncodingError; +const IdentityElementError = crypto.errors.IdentityElementError; +const NonCanonicalError = crypto.errors.NonCanonicalError; +const NotSquareError = crypto.errors.NotSquareError; + +/// Group operations over P256. +pub const P256 = struct { + /// The underlying prime field. + pub const Fe = @import("p256/field.zig").Fe; + /// Field arithmetic mod the order of the main subgroup. + pub const scalar = @import("p256/scalar.zig"); + + x: Fe, + y: Fe, + z: Fe = Fe.one, + + is_base: bool = false, + + /// The P256 base point. + pub const basePoint = P256{ + .x = try Fe.fromInt(48439561293906451759052585252797914202762949526041747995844080717082404635286), + .y = try Fe.fromInt(36134250956749795798585127919587881956611106672985015071877198253568414405109), + .z = Fe.one, + .is_base = true, + }; + + /// The P256 neutral element. + pub const identityElement = P256{ .x = Fe.zero, .y = Fe.one, .z = Fe.zero }; + + pub const B = try Fe.fromInt(41058363725152142129326129780047268409114441015993725554835256314039467401291); + + /// Reject the neutral element. + pub fn rejectIdentity(p: P256) IdentityElementError!void { + if (p.x.isZero()) { + return error.IdentityElement; + } + } + + /// Create a point from affine coordinates after checking that they match the curve equation. + pub fn fromAffineCoordinates(x: Fe, y: Fe) EncodingError!P256 { + const x3AxB = x.sq().mul(x).sub(x).sub(x).sub(x).add(B); + const yy = y.sq(); + if (!x3AxB.equivalent(yy)) { + return error.InvalidEncoding; + } + const p: P256 = .{ .x = x, .y = y, .z = Fe.one }; + return p; + } + + /// Create a point from serialized affine coordinates. + pub fn fromSerializedAffineCoordinates(xs: [32]u8, ys: [32]u8, endian: builtin.Endian) (NonCanonicalError || EncodingError)!P256 { + const x = try Fe.fromBytes(xs, endian); + const y = try Fe.fromBytes(ys, endian); + return fromAffineCoordinates(x, y); + } + + /// Recover the Y coordinate from the X coordinate. + pub fn recoverY(x: Fe, is_odd: bool) NotSquareError!Fe { + const x3AxB = x.sq().mul(x).sub(x).sub(x).sub(x).add(B); + var y = try x3AxB.sqrt(); + const yn = y.neg(); + y.cMov(yn, @boolToInt(is_odd) ^ @boolToInt(y.isOdd())); + return y; + } + + /// Deserialize a SEC1-encoded point. + pub fn fromSec1(s: []const u8) (EncodingError || NotSquareError || NonCanonicalError)!P256 { + if (s.len < 1) return error.InvalidEncoding; + const encoding_type = s[0]; + const encoded = s[1..]; + switch (encoding_type) { + 0 => { + if (encoded.len != 0) return error.InvalidEncoding; + return P256.identityElement; + }, + 2, 3 => { + if (encoded.len != 32) return error.InvalidEncoding; + const x = try Fe.fromBytes(encoded[0..32].*, .Big); + const y_is_odd = (encoding_type == 3); + const y = try recoverY(x, y_is_odd); + return P256{ .x = x, .y = y }; + }, + 4 => { + if (encoded.len != 64) return error.InvalidEncoding; + const x = try Fe.fromBytes(encoded[0..32].*, .Big); + const y = try Fe.fromBytes(encoded[32..64].*, .Big); + return P256.fromAffineCoordinates(x, y); + }, + else => return error.InvalidEncoding, + } + } + + /// Serialize a point using the compressed SEC-1 format. + pub fn toCompressedSec1(p: P256) [33]u8 { + var out: [33]u8 = undefined; + const xy = p.affineCoordinates(); + out[0] = if (xy.y.isOdd()) 3 else 2; + mem.copy(u8, out[1..], &xy.x.toBytes(.Big)); + return out; + } + + /// Serialize a point using the uncompressed SEC-1 format. + pub fn toUncompressedSec1(p: P256) [65]u8 { + var out: [65]u8 = undefined; + out[0] = 4; + const xy = p.affineCoordinates(); + mem.copy(u8, out[1..33], &xy.x.toBytes(.Big)); + mem.copy(u8, out[33..65], &xy.y.toBytes(.Big)); + return out; + } + + /// Return a random point. + pub fn random() P256 { + const n = scalar.random(.Little); + return basePoint.mul(n, .Little) catch unreachable; + } + + /// Flip the sign of the X coordinate. + pub fn neg(p: P256) P256 { + return .{ .x = p.x, .y = p.y.neg(), .z = p.z }; + } + + /// Double a P256 point. + // Algorithm 6 from https://eprint.iacr.org/2015/1060.pdf + pub fn dbl(p: P256) P256 { + var t0 = p.x.sq(); + var t1 = p.y.sq(); + var t2 = p.z.sq(); + var t3 = p.x.mul(p.y); + t3 = t3.dbl(); + var Z3 = p.x.mul(p.z); + Z3 = Z3.add(Z3); + var Y3 = B.mul(t2); + Y3 = Y3.sub(Z3); + var X3 = Y3.dbl(); + Y3 = X3.add(Y3); + X3 = t1.sub(Y3); + Y3 = t1.add(Y3); + Y3 = X3.mul(Y3); + X3 = X3.mul(t3); + t3 = t2.dbl(); + t2 = t2.add(t3); + Z3 = B.mul(Z3); + Z3 = Z3.sub(t2); + Z3 = Z3.sub(t0); + t3 = Z3.dbl(); + Z3 = Z3.add(t3); + t3 = t0.dbl(); + t0 = t3.add(t0); + t0 = t0.sub(t2); + t0 = t0.mul(Z3); + Y3 = Y3.add(t0); + t0 = p.y.mul(p.z); + t0 = t0.dbl(); + Z3 = t0.mul(Z3); + X3 = X3.sub(Z3); + Z3 = t0.mul(t1); + Z3 = Z3.dbl().dbl(); + return .{ + .x = X3, + .y = Y3, + .z = Z3, + }; + } + + /// Add P256 points, the second being specified using affine coordinates. + // Algorithm 5 from https://eprint.iacr.org/2015/1060.pdf + pub fn addMixed(p: P256, q: struct { x: Fe, y: Fe }) P256 { + var t0 = p.x.mul(q.x); + var t1 = p.y.mul(q.y); + var t3 = q.x.add(q.y); + var t4 = p.x.add(p.y); + t3 = t3.mul(t4); + t4 = t0.add(t1); + t3 = t3.sub(t4); + t4 = q.y.mul(p.z); + t4 = t4.add(p.y); + var Y3 = q.x.mul(p.z); + Y3 = Y3.add(p.x); + var Z3 = B.mul(p.z); + var X3 = Y3.sub(Z3); + Z3 = X3.dbl(); + X3 = X3.add(Z3); + Z3 = t1.sub(X3); + X3 = t1.dbl(); + Y3 = B.mul(Y3); + t1 = p.z.add(p.z); + var t2 = t1.add(p.z); + Y3 = Y3.sub(t2); + Y3 = Y3.sub(t0); + t1 = Y3.dbl(); + Y3 = t1.add(Y3); + t1 = t0.dbl(); + t0 = t1.add(t0); + t0 = t0.sub(t2); + t1 = t4.mul(Y3); + t2 = t0.mul(Y3); + Y3 = X3.mul(Z3); + Y3 = Y3.add(t2); + X3 = t3.mul(X3); + X3 = X3.sub(t1); + Z3 = t4.mul(Z3); + t1 = t3.mul(t0); + Z3 = Z3.add(t1); + return .{ + .x = X3, + .y = Y3, + .z = Z3, + }; + } + + // Add P256 points. + // Algorithm 4 from https://eprint.iacr.org/2015/1060.pdf + pub fn add(p: P256, q: P256) P256 { + var t0 = p.x.mul(q.x); + var t1 = p.y.mul(q.y); + var t2 = p.z.mul(q.z); + var t3 = p.x.add(p.y); + var t4 = q.x.add(q.y); + t3 = t3.mul(t4); + t4 = t0.add(t1); + t3 = t3.sub(t4); + t4 = p.y.add(p.z); + var X3 = q.y.add(q.z); + t4 = t4.mul(X3); + X3 = t1.add(t2); + t4 = t4.sub(X3); + X3 = p.x.add(p.z); + var Y3 = q.x.add(q.z); + X3 = X3.mul(Y3); + Y3 = t0.add(t2); + Y3 = X3.sub(Y3); + var Z3 = B.mul(t2); + X3 = Y3.sub(Z3); + Z3 = X3.dbl(); + X3 = X3.add(Z3); + Z3 = t1.sub(X3); + X3 = t1.add(X3); + Y3 = B.mul(Y3); + t1 = t2.dbl(); + t2 = t1.add(t2); + Y3 = Y3.sub(t2); + Y3 = Y3.sub(t0); + t1 = Y3.dbl(); + Y3 = t1.add(Y3); + t1 = t0.dbl(); + t0 = t1.add(t0); + t0 = t0.sub(t2); + t1 = t4.mul(Y3); + t2 = t0.mul(Y3); + Y3 = X3.mul(Z3); + Y3 = Y3.add(t2); + X3 = t3.mul(X3); + X3 = X3.sub(t1); + Z3 = t4.mul(Z3); + t1 = t3.mul(t0); + Z3 = Z3.add(t1); + return .{ + .x = X3, + .y = Y3, + .z = Z3, + }; + } + + // Subtract P256 points. + pub fn sub(p: P256, q: P256) P256 { + return p.add(q.neg()); + } + + /// Return affine coordinates. + pub fn affineCoordinates(p: P256) struct { x: Fe, y: Fe } { + const zinv = p.z.invert(); + const ret = .{ + .x = p.x.mul(zinv), + .y = p.y.mul(zinv), + }; + return ret; + } + + /// Return true if both coordinate sets represent the same point. + pub fn equivalent(a: P256, b: P256) bool { + if (a.sub(b).rejectIdentity()) { + return false; + } else |_| { + return true; + } + } + + fn cMov(p: *P256, a: P256, c: u1) void { + p.x.cMov(a.x, c); + p.y.cMov(a.y, c); + p.z.cMov(a.z, c); + } + + fn pcSelect(comptime n: usize, pc: [n]P256, b: u8) P256 { + var t = P256.identityElement; + comptime var i: u8 = 1; + inline while (i < pc.len) : (i += 1) { + t.cMov(pc[i], @truncate(u1, (@as(usize, b ^ i) -% 1) >> 8)); + } + return t; + } + + fn slide(s: [32]u8) [2 * 32 + 1]i8 { + var e: [2 * 32 + 1]i8 = undefined; + for (s) |x, i| { + e[i * 2 + 0] = @as(i8, @truncate(u4, x)); + e[i * 2 + 1] = @as(i8, @truncate(u4, x >> 4)); + } + // Now, e[0..63] is between 0 and 15, e[63] is between 0 and 7 + var carry: i8 = 0; + for (e[0..64]) |*x| { + x.* += carry; + carry = (x.* + 8) >> 4; + x.* -= carry * 16; + std.debug.assert(x.* >= -8 and x.* <= 8); + } + e[64] = carry; + // Now, e[*] is between -8 and 8, including e[64] + std.debug.assert(carry >= -8 and carry <= 8); + return e; + } + + fn pcMul(pc: [9]P256, s: [32]u8, comptime vartime: bool) IdentityElementError!P256 { + std.debug.assert(vartime); + const e = slide(s); + var q = P256.identityElement; + var pos = e.len - 1; + while (true) : (pos -= 1) { + const slot = e[pos]; + if (slot > 0) { + q = q.add(pc[@intCast(usize, slot)]); + } else if (slot < 0) { + q = q.sub(pc[@intCast(usize, -slot)]); + } + if (pos == 0) break; + q = q.dbl().dbl().dbl().dbl(); + } + try q.rejectIdentity(); + return q; + } + + fn pcMul16(pc: [16]P256, s: [32]u8, comptime vartime: bool) IdentityElementError!P256 { + var q = P256.identityElement; + var pos: usize = 252; + while (true) : (pos -= 4) { + const slot = @truncate(u4, (s[pos >> 3] >> @truncate(u3, pos))); + if (vartime) { + if (slot != 0) { + q = q.add(pc[slot]); + } + } else { + q = q.add(pcSelect(16, pc, slot)); + } + if (pos == 0) break; + q = q.dbl().dbl().dbl().dbl(); + } + try q.rejectIdentity(); + return q; + } + + fn precompute(p: P256, comptime count: usize) [1 + count]P256 { + var pc: [1 + count]P256 = undefined; + pc[0] = P256.identityElement; + pc[1] = p; + var i: usize = 2; + while (i <= count) : (i += 1) { + pc[i] = if (i % 2 == 0) pc[i / 2].dbl() else pc[i - 1].add(p); + } + return pc; + } + + /// Multiply an elliptic curve point by a scalar. + /// Return error.IdentityElement if the result is the identity element. + pub fn mul(p: P256, s_: [32]u8, endian: builtin.Endian) IdentityElementError!P256 { + const s = if (endian == .Little) s_ else Fe.orderSwap(s_); + const pc = if (p.is_base) precompute(P256.basePoint, 15) else pc: { + try p.rejectIdentity(); + const xpc = precompute(p, 15); + break :pc xpc; + }; + return pcMul16(pc, s, false); + } + + /// Multiply an elliptic curve point by a *PUBLIC* scalar *IN VARIABLE TIME* + /// This can be used for signature verification. + pub fn mulPublic(p: P256, s_: [32]u8, endian: builtin.Endian) IdentityElementError!P256 { + const s = if (endian == .Little) s_ else Fe.orderSwap(s_); + const pc = if (p.is_base) precompute(P256.basePoint, 8) else pc: { + try p.rejectIdentity(); + const xpc = precompute(p, 8); + break :pc xpc; + }; + return pcMul(pc, s, true); + } +}; + +test "p256" { + _ = @import("tests.zig"); +} diff --git a/zig/lib/std/crypto/pcurves/p256/field.zig b/zig/lib/std/crypto/pcurves/p256/field.zig new file mode 100644 index 0000000000..15de1fe43e --- /dev/null +++ b/zig/lib/std/crypto/pcurves/p256/field.zig @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2021 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. + +const std = @import("std"); +const common = @import("../common.zig"); + +const Field = common.Field; + +pub const Fe = Field(.{ + .fiat = @import("p256_64.zig"), + .field_order = 115792089210356248762697446949407573530086143415290314195533631308867097853951, + .field_bits = 256, + .saturated_bits = 255, + .encoded_length = 32, +}); diff --git a/zig/lib/std/crypto/pcurves/p256/p256_64.zig b/zig/lib/std/crypto/pcurves/p256/p256_64.zig new file mode 100644 index 0000000000..ca04ee961d --- /dev/null +++ b/zig/lib/std/crypto/pcurves/p256/p256_64.zig @@ -0,0 +1,1812 @@ +// Autogenerated: 'src/ExtractionOCaml/word_by_word_montgomery' --lang Zig --internal-static --public-function-case camelCase --private-function-case camelCase --no-prefix-fiat --package-name p256 '' 64 '2^256 - 2^224 + 2^192 + 2^96 - 1' mul square add sub opp from_montgomery to_montgomery nonzero selectznz to_bytes from_bytes one msat divstep divstep_precomp +// curve description (via package name): p256 +// machine_wordsize = 64 (from "64") +// requested operations: mul, square, add, sub, opp, from_montgomery, to_montgomery, nonzero, selectznz, to_bytes, from_bytes, one, msat, divstep, divstep_precomp +// m = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff (from "2^256 - 2^224 + 2^192 + 2^96 - 1") +// +// NOTE: In addition to the bounds specified above each function, all +// functions synthesized for this Montgomery arithmetic require the +// input to be strictly less than the prime modulus (m), and also +// require the input to be in the unique saturated representation. +// All functions also ensure that these two properties are true of +// return values. +// +// Computed values: +// eval z = z[0] + (z[1] << 64) + (z[2] << 128) + (z[3] << 192) +// bytes_eval z = z[0] + (z[1] << 8) + (z[2] << 16) + (z[3] << 24) + (z[4] << 32) + (z[5] << 40) + (z[6] << 48) + (z[7] << 56) + (z[8] << 64) + (z[9] << 72) + (z[10] << 80) + (z[11] << 88) + (z[12] << 96) + (z[13] << 104) + (z[14] << 112) + (z[15] << 120) + (z[16] << 128) + (z[17] << 136) + (z[18] << 144) + (z[19] << 152) + (z[20] << 160) + (z[21] << 168) + (z[22] << 176) + (z[23] << 184) + (z[24] << 192) + (z[25] << 200) + (z[26] << 208) + (z[27] << 216) + (z[28] << 224) + (z[29] << 232) + (z[30] << 240) + (z[31] << 248) +// twos_complement_eval z = let x1 := z[0] + (z[1] << 64) + (z[2] << 128) + (z[3] << 192) in +// if x1 & (2^256-1) < 2^255 then x1 & (2^256-1) else (x1 & (2^256-1)) - 2^256 + +const std = @import("std"); +const cast = std.meta.cast; +const mode = std.builtin.mode; // Checked arithmetic is disabled in non-debug modes to avoid side channels + +pub const Limbs = [4]u64; + +/// The function addcarryxU64 is an addition with carry. +/// Postconditions: +/// out1 = (arg1 + arg2 + arg3) mod 2^64 +/// out2 = ⌊(arg1 + arg2 + arg3) / 2^64⌋ +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// arg3: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [0x0 ~> 0x1] +fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) callconv(.Inline) void { + @setRuntimeSafety(mode == .Debug); + + var t: u64 = undefined; + const carry1 = @addWithOverflow(u64, arg2, arg3, &t); + const carry2 = @addWithOverflow(u64, t, arg1, out1); + out2.* = @boolToInt(carry1) | @boolToInt(carry2); +} + +/// The function subborrowxU64 is a subtraction with borrow. +/// Postconditions: +/// out1 = (-arg1 + arg2 + -arg3) mod 2^64 +/// out2 = -⌊(-arg1 + arg2 + -arg3) / 2^64⌋ +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// arg3: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [0x0 ~> 0x1] +fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) callconv(.Inline) void { + @setRuntimeSafety(mode == .Debug); + + var t: u64 = undefined; + const carry1 = @subWithOverflow(u64, arg2, arg3, &t); + const carry2 = @subWithOverflow(u64, t, arg1, out1); + out2.* = @boolToInt(carry1) | @boolToInt(carry2); +} + +/// The function mulxU64 is a multiplication, returning the full double-width result. +/// Postconditions: +/// out1 = (arg1 * arg2) mod 2^64 +/// out2 = ⌊arg1 * arg2 / 2^64⌋ +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0xffffffffffffffff] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [0x0 ~> 0xffffffffffffffff] +fn mulxU64(out1: *u64, out2: *u64, arg1: u64, arg2: u64) callconv(.Inline) void { + @setRuntimeSafety(mode == .Debug); + + const x = @as(u128, arg1) * @as(u128, arg2); + out1.* = @truncate(u64, x); + out2.* = @truncate(u64, x >> 64); +} + +/// The function cmovznzU64 is a single-word conditional move. +/// Postconditions: +/// out1 = (if arg1 = 0 then arg2 else arg3) +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// arg3: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +fn cmovznzU64(out1: *u64, arg1: u1, arg2: u64, arg3: u64) callconv(.Inline) void { + @setRuntimeSafety(mode == .Debug); + + const mask = 0 -% @as(u64, arg1); + out1.* = (mask & arg3) | ((~mask) & arg2); +} + +/// The function mul multiplies two field elements in the Montgomery domain. +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// 0 ≤ eval arg2 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg2)) mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn mul(out1: *[4]u64, arg1: [4]u64, arg2: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[1]); + const x2 = (arg1[2]); + const x3 = (arg1[3]); + const x4 = (arg1[0]); + var x5: u64 = undefined; + var x6: u64 = undefined; + mulxU64(&x5, &x6, x4, (arg2[3])); + var x7: u64 = undefined; + var x8: u64 = undefined; + mulxU64(&x7, &x8, x4, (arg2[2])); + var x9: u64 = undefined; + var x10: u64 = undefined; + mulxU64(&x9, &x10, x4, (arg2[1])); + var x11: u64 = undefined; + var x12: u64 = undefined; + mulxU64(&x11, &x12, x4, (arg2[0])); + var x13: u64 = undefined; + var x14: u1 = undefined; + addcarryxU64(&x13, &x14, 0x0, x12, x9); + var x15: u64 = undefined; + var x16: u1 = undefined; + addcarryxU64(&x15, &x16, x14, x10, x7); + var x17: u64 = undefined; + var x18: u1 = undefined; + addcarryxU64(&x17, &x18, x16, x8, x5); + const x19 = (cast(u64, x18) + x6); + var x20: u64 = undefined; + var x21: u64 = undefined; + mulxU64(&x20, &x21, x11, 0xffffffff00000001); + var x22: u64 = undefined; + var x23: u64 = undefined; + mulxU64(&x22, &x23, x11, 0xffffffff); + var x24: u64 = undefined; + var x25: u64 = undefined; + mulxU64(&x24, &x25, x11, 0xffffffffffffffff); + var x26: u64 = undefined; + var x27: u1 = undefined; + addcarryxU64(&x26, &x27, 0x0, x25, x22); + const x28 = (cast(u64, x27) + x23); + var x29: u64 = undefined; + var x30: u1 = undefined; + addcarryxU64(&x29, &x30, 0x0, x11, x24); + var x31: u64 = undefined; + var x32: u1 = undefined; + addcarryxU64(&x31, &x32, x30, x13, x26); + var x33: u64 = undefined; + var x34: u1 = undefined; + addcarryxU64(&x33, &x34, x32, x15, x28); + var x35: u64 = undefined; + var x36: u1 = undefined; + addcarryxU64(&x35, &x36, x34, x17, x20); + var x37: u64 = undefined; + var x38: u1 = undefined; + addcarryxU64(&x37, &x38, x36, x19, x21); + var x39: u64 = undefined; + var x40: u64 = undefined; + mulxU64(&x39, &x40, x1, (arg2[3])); + var x41: u64 = undefined; + var x42: u64 = undefined; + mulxU64(&x41, &x42, x1, (arg2[2])); + var x43: u64 = undefined; + var x44: u64 = undefined; + mulxU64(&x43, &x44, x1, (arg2[1])); + var x45: u64 = undefined; + var x46: u64 = undefined; + mulxU64(&x45, &x46, x1, (arg2[0])); + var x47: u64 = undefined; + var x48: u1 = undefined; + addcarryxU64(&x47, &x48, 0x0, x46, x43); + var x49: u64 = undefined; + var x50: u1 = undefined; + addcarryxU64(&x49, &x50, x48, x44, x41); + var x51: u64 = undefined; + var x52: u1 = undefined; + addcarryxU64(&x51, &x52, x50, x42, x39); + const x53 = (cast(u64, x52) + x40); + var x54: u64 = undefined; + var x55: u1 = undefined; + addcarryxU64(&x54, &x55, 0x0, x31, x45); + var x56: u64 = undefined; + var x57: u1 = undefined; + addcarryxU64(&x56, &x57, x55, x33, x47); + var x58: u64 = undefined; + var x59: u1 = undefined; + addcarryxU64(&x58, &x59, x57, x35, x49); + var x60: u64 = undefined; + var x61: u1 = undefined; + addcarryxU64(&x60, &x61, x59, x37, x51); + var x62: u64 = undefined; + var x63: u1 = undefined; + addcarryxU64(&x62, &x63, x61, cast(u64, x38), x53); + var x64: u64 = undefined; + var x65: u64 = undefined; + mulxU64(&x64, &x65, x54, 0xffffffff00000001); + var x66: u64 = undefined; + var x67: u64 = undefined; + mulxU64(&x66, &x67, x54, 0xffffffff); + var x68: u64 = undefined; + var x69: u64 = undefined; + mulxU64(&x68, &x69, x54, 0xffffffffffffffff); + var x70: u64 = undefined; + var x71: u1 = undefined; + addcarryxU64(&x70, &x71, 0x0, x69, x66); + const x72 = (cast(u64, x71) + x67); + var x73: u64 = undefined; + var x74: u1 = undefined; + addcarryxU64(&x73, &x74, 0x0, x54, x68); + var x75: u64 = undefined; + var x76: u1 = undefined; + addcarryxU64(&x75, &x76, x74, x56, x70); + var x77: u64 = undefined; + var x78: u1 = undefined; + addcarryxU64(&x77, &x78, x76, x58, x72); + var x79: u64 = undefined; + var x80: u1 = undefined; + addcarryxU64(&x79, &x80, x78, x60, x64); + var x81: u64 = undefined; + var x82: u1 = undefined; + addcarryxU64(&x81, &x82, x80, x62, x65); + const x83 = (cast(u64, x82) + cast(u64, x63)); + var x84: u64 = undefined; + var x85: u64 = undefined; + mulxU64(&x84, &x85, x2, (arg2[3])); + var x86: u64 = undefined; + var x87: u64 = undefined; + mulxU64(&x86, &x87, x2, (arg2[2])); + var x88: u64 = undefined; + var x89: u64 = undefined; + mulxU64(&x88, &x89, x2, (arg2[1])); + var x90: u64 = undefined; + var x91: u64 = undefined; + mulxU64(&x90, &x91, x2, (arg2[0])); + var x92: u64 = undefined; + var x93: u1 = undefined; + addcarryxU64(&x92, &x93, 0x0, x91, x88); + var x94: u64 = undefined; + var x95: u1 = undefined; + addcarryxU64(&x94, &x95, x93, x89, x86); + var x96: u64 = undefined; + var x97: u1 = undefined; + addcarryxU64(&x96, &x97, x95, x87, x84); + const x98 = (cast(u64, x97) + x85); + var x99: u64 = undefined; + var x100: u1 = undefined; + addcarryxU64(&x99, &x100, 0x0, x75, x90); + var x101: u64 = undefined; + var x102: u1 = undefined; + addcarryxU64(&x101, &x102, x100, x77, x92); + var x103: u64 = undefined; + var x104: u1 = undefined; + addcarryxU64(&x103, &x104, x102, x79, x94); + var x105: u64 = undefined; + var x106: u1 = undefined; + addcarryxU64(&x105, &x106, x104, x81, x96); + var x107: u64 = undefined; + var x108: u1 = undefined; + addcarryxU64(&x107, &x108, x106, x83, x98); + var x109: u64 = undefined; + var x110: u64 = undefined; + mulxU64(&x109, &x110, x99, 0xffffffff00000001); + var x111: u64 = undefined; + var x112: u64 = undefined; + mulxU64(&x111, &x112, x99, 0xffffffff); + var x113: u64 = undefined; + var x114: u64 = undefined; + mulxU64(&x113, &x114, x99, 0xffffffffffffffff); + var x115: u64 = undefined; + var x116: u1 = undefined; + addcarryxU64(&x115, &x116, 0x0, x114, x111); + const x117 = (cast(u64, x116) + x112); + var x118: u64 = undefined; + var x119: u1 = undefined; + addcarryxU64(&x118, &x119, 0x0, x99, x113); + var x120: u64 = undefined; + var x121: u1 = undefined; + addcarryxU64(&x120, &x121, x119, x101, x115); + var x122: u64 = undefined; + var x123: u1 = undefined; + addcarryxU64(&x122, &x123, x121, x103, x117); + var x124: u64 = undefined; + var x125: u1 = undefined; + addcarryxU64(&x124, &x125, x123, x105, x109); + var x126: u64 = undefined; + var x127: u1 = undefined; + addcarryxU64(&x126, &x127, x125, x107, x110); + const x128 = (cast(u64, x127) + cast(u64, x108)); + var x129: u64 = undefined; + var x130: u64 = undefined; + mulxU64(&x129, &x130, x3, (arg2[3])); + var x131: u64 = undefined; + var x132: u64 = undefined; + mulxU64(&x131, &x132, x3, (arg2[2])); + var x133: u64 = undefined; + var x134: u64 = undefined; + mulxU64(&x133, &x134, x3, (arg2[1])); + var x135: u64 = undefined; + var x136: u64 = undefined; + mulxU64(&x135, &x136, x3, (arg2[0])); + var x137: u64 = undefined; + var x138: u1 = undefined; + addcarryxU64(&x137, &x138, 0x0, x136, x133); + var x139: u64 = undefined; + var x140: u1 = undefined; + addcarryxU64(&x139, &x140, x138, x134, x131); + var x141: u64 = undefined; + var x142: u1 = undefined; + addcarryxU64(&x141, &x142, x140, x132, x129); + const x143 = (cast(u64, x142) + x130); + var x144: u64 = undefined; + var x145: u1 = undefined; + addcarryxU64(&x144, &x145, 0x0, x120, x135); + var x146: u64 = undefined; + var x147: u1 = undefined; + addcarryxU64(&x146, &x147, x145, x122, x137); + var x148: u64 = undefined; + var x149: u1 = undefined; + addcarryxU64(&x148, &x149, x147, x124, x139); + var x150: u64 = undefined; + var x151: u1 = undefined; + addcarryxU64(&x150, &x151, x149, x126, x141); + var x152: u64 = undefined; + var x153: u1 = undefined; + addcarryxU64(&x152, &x153, x151, x128, x143); + var x154: u64 = undefined; + var x155: u64 = undefined; + mulxU64(&x154, &x155, x144, 0xffffffff00000001); + var x156: u64 = undefined; + var x157: u64 = undefined; + mulxU64(&x156, &x157, x144, 0xffffffff); + var x158: u64 = undefined; + var x159: u64 = undefined; + mulxU64(&x158, &x159, x144, 0xffffffffffffffff); + var x160: u64 = undefined; + var x161: u1 = undefined; + addcarryxU64(&x160, &x161, 0x0, x159, x156); + const x162 = (cast(u64, x161) + x157); + var x163: u64 = undefined; + var x164: u1 = undefined; + addcarryxU64(&x163, &x164, 0x0, x144, x158); + var x165: u64 = undefined; + var x166: u1 = undefined; + addcarryxU64(&x165, &x166, x164, x146, x160); + var x167: u64 = undefined; + var x168: u1 = undefined; + addcarryxU64(&x167, &x168, x166, x148, x162); + var x169: u64 = undefined; + var x170: u1 = undefined; + addcarryxU64(&x169, &x170, x168, x150, x154); + var x171: u64 = undefined; + var x172: u1 = undefined; + addcarryxU64(&x171, &x172, x170, x152, x155); + const x173 = (cast(u64, x172) + cast(u64, x153)); + var x174: u64 = undefined; + var x175: u1 = undefined; + subborrowxU64(&x174, &x175, 0x0, x165, 0xffffffffffffffff); + var x176: u64 = undefined; + var x177: u1 = undefined; + subborrowxU64(&x176, &x177, x175, x167, 0xffffffff); + var x178: u64 = undefined; + var x179: u1 = undefined; + subborrowxU64(&x178, &x179, x177, x169, cast(u64, 0x0)); + var x180: u64 = undefined; + var x181: u1 = undefined; + subborrowxU64(&x180, &x181, x179, x171, 0xffffffff00000001); + var x182: u64 = undefined; + var x183: u1 = undefined; + subborrowxU64(&x182, &x183, x181, x173, cast(u64, 0x0)); + var x184: u64 = undefined; + cmovznzU64(&x184, x183, x174, x165); + var x185: u64 = undefined; + cmovznzU64(&x185, x183, x176, x167); + var x186: u64 = undefined; + cmovznzU64(&x186, x183, x178, x169); + var x187: u64 = undefined; + cmovznzU64(&x187, x183, x180, x171); + out1[0] = x184; + out1[1] = x185; + out1[2] = x186; + out1[3] = x187; +} + +/// The function square squares a field element in the Montgomery domain. +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg1)) mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn square(out1: *[4]u64, arg1: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[1]); + const x2 = (arg1[2]); + const x3 = (arg1[3]); + const x4 = (arg1[0]); + var x5: u64 = undefined; + var x6: u64 = undefined; + mulxU64(&x5, &x6, x4, (arg1[3])); + var x7: u64 = undefined; + var x8: u64 = undefined; + mulxU64(&x7, &x8, x4, (arg1[2])); + var x9: u64 = undefined; + var x10: u64 = undefined; + mulxU64(&x9, &x10, x4, (arg1[1])); + var x11: u64 = undefined; + var x12: u64 = undefined; + mulxU64(&x11, &x12, x4, (arg1[0])); + var x13: u64 = undefined; + var x14: u1 = undefined; + addcarryxU64(&x13, &x14, 0x0, x12, x9); + var x15: u64 = undefined; + var x16: u1 = undefined; + addcarryxU64(&x15, &x16, x14, x10, x7); + var x17: u64 = undefined; + var x18: u1 = undefined; + addcarryxU64(&x17, &x18, x16, x8, x5); + const x19 = (cast(u64, x18) + x6); + var x20: u64 = undefined; + var x21: u64 = undefined; + mulxU64(&x20, &x21, x11, 0xffffffff00000001); + var x22: u64 = undefined; + var x23: u64 = undefined; + mulxU64(&x22, &x23, x11, 0xffffffff); + var x24: u64 = undefined; + var x25: u64 = undefined; + mulxU64(&x24, &x25, x11, 0xffffffffffffffff); + var x26: u64 = undefined; + var x27: u1 = undefined; + addcarryxU64(&x26, &x27, 0x0, x25, x22); + const x28 = (cast(u64, x27) + x23); + var x29: u64 = undefined; + var x30: u1 = undefined; + addcarryxU64(&x29, &x30, 0x0, x11, x24); + var x31: u64 = undefined; + var x32: u1 = undefined; + addcarryxU64(&x31, &x32, x30, x13, x26); + var x33: u64 = undefined; + var x34: u1 = undefined; + addcarryxU64(&x33, &x34, x32, x15, x28); + var x35: u64 = undefined; + var x36: u1 = undefined; + addcarryxU64(&x35, &x36, x34, x17, x20); + var x37: u64 = undefined; + var x38: u1 = undefined; + addcarryxU64(&x37, &x38, x36, x19, x21); + var x39: u64 = undefined; + var x40: u64 = undefined; + mulxU64(&x39, &x40, x1, (arg1[3])); + var x41: u64 = undefined; + var x42: u64 = undefined; + mulxU64(&x41, &x42, x1, (arg1[2])); + var x43: u64 = undefined; + var x44: u64 = undefined; + mulxU64(&x43, &x44, x1, (arg1[1])); + var x45: u64 = undefined; + var x46: u64 = undefined; + mulxU64(&x45, &x46, x1, (arg1[0])); + var x47: u64 = undefined; + var x48: u1 = undefined; + addcarryxU64(&x47, &x48, 0x0, x46, x43); + var x49: u64 = undefined; + var x50: u1 = undefined; + addcarryxU64(&x49, &x50, x48, x44, x41); + var x51: u64 = undefined; + var x52: u1 = undefined; + addcarryxU64(&x51, &x52, x50, x42, x39); + const x53 = (cast(u64, x52) + x40); + var x54: u64 = undefined; + var x55: u1 = undefined; + addcarryxU64(&x54, &x55, 0x0, x31, x45); + var x56: u64 = undefined; + var x57: u1 = undefined; + addcarryxU64(&x56, &x57, x55, x33, x47); + var x58: u64 = undefined; + var x59: u1 = undefined; + addcarryxU64(&x58, &x59, x57, x35, x49); + var x60: u64 = undefined; + var x61: u1 = undefined; + addcarryxU64(&x60, &x61, x59, x37, x51); + var x62: u64 = undefined; + var x63: u1 = undefined; + addcarryxU64(&x62, &x63, x61, cast(u64, x38), x53); + var x64: u64 = undefined; + var x65: u64 = undefined; + mulxU64(&x64, &x65, x54, 0xffffffff00000001); + var x66: u64 = undefined; + var x67: u64 = undefined; + mulxU64(&x66, &x67, x54, 0xffffffff); + var x68: u64 = undefined; + var x69: u64 = undefined; + mulxU64(&x68, &x69, x54, 0xffffffffffffffff); + var x70: u64 = undefined; + var x71: u1 = undefined; + addcarryxU64(&x70, &x71, 0x0, x69, x66); + const x72 = (cast(u64, x71) + x67); + var x73: u64 = undefined; + var x74: u1 = undefined; + addcarryxU64(&x73, &x74, 0x0, x54, x68); + var x75: u64 = undefined; + var x76: u1 = undefined; + addcarryxU64(&x75, &x76, x74, x56, x70); + var x77: u64 = undefined; + var x78: u1 = undefined; + addcarryxU64(&x77, &x78, x76, x58, x72); + var x79: u64 = undefined; + var x80: u1 = undefined; + addcarryxU64(&x79, &x80, x78, x60, x64); + var x81: u64 = undefined; + var x82: u1 = undefined; + addcarryxU64(&x81, &x82, x80, x62, x65); + const x83 = (cast(u64, x82) + cast(u64, x63)); + var x84: u64 = undefined; + var x85: u64 = undefined; + mulxU64(&x84, &x85, x2, (arg1[3])); + var x86: u64 = undefined; + var x87: u64 = undefined; + mulxU64(&x86, &x87, x2, (arg1[2])); + var x88: u64 = undefined; + var x89: u64 = undefined; + mulxU64(&x88, &x89, x2, (arg1[1])); + var x90: u64 = undefined; + var x91: u64 = undefined; + mulxU64(&x90, &x91, x2, (arg1[0])); + var x92: u64 = undefined; + var x93: u1 = undefined; + addcarryxU64(&x92, &x93, 0x0, x91, x88); + var x94: u64 = undefined; + var x95: u1 = undefined; + addcarryxU64(&x94, &x95, x93, x89, x86); + var x96: u64 = undefined; + var x97: u1 = undefined; + addcarryxU64(&x96, &x97, x95, x87, x84); + const x98 = (cast(u64, x97) + x85); + var x99: u64 = undefined; + var x100: u1 = undefined; + addcarryxU64(&x99, &x100, 0x0, x75, x90); + var x101: u64 = undefined; + var x102: u1 = undefined; + addcarryxU64(&x101, &x102, x100, x77, x92); + var x103: u64 = undefined; + var x104: u1 = undefined; + addcarryxU64(&x103, &x104, x102, x79, x94); + var x105: u64 = undefined; + var x106: u1 = undefined; + addcarryxU64(&x105, &x106, x104, x81, x96); + var x107: u64 = undefined; + var x108: u1 = undefined; + addcarryxU64(&x107, &x108, x106, x83, x98); + var x109: u64 = undefined; + var x110: u64 = undefined; + mulxU64(&x109, &x110, x99, 0xffffffff00000001); + var x111: u64 = undefined; + var x112: u64 = undefined; + mulxU64(&x111, &x112, x99, 0xffffffff); + var x113: u64 = undefined; + var x114: u64 = undefined; + mulxU64(&x113, &x114, x99, 0xffffffffffffffff); + var x115: u64 = undefined; + var x116: u1 = undefined; + addcarryxU64(&x115, &x116, 0x0, x114, x111); + const x117 = (cast(u64, x116) + x112); + var x118: u64 = undefined; + var x119: u1 = undefined; + addcarryxU64(&x118, &x119, 0x0, x99, x113); + var x120: u64 = undefined; + var x121: u1 = undefined; + addcarryxU64(&x120, &x121, x119, x101, x115); + var x122: u64 = undefined; + var x123: u1 = undefined; + addcarryxU64(&x122, &x123, x121, x103, x117); + var x124: u64 = undefined; + var x125: u1 = undefined; + addcarryxU64(&x124, &x125, x123, x105, x109); + var x126: u64 = undefined; + var x127: u1 = undefined; + addcarryxU64(&x126, &x127, x125, x107, x110); + const x128 = (cast(u64, x127) + cast(u64, x108)); + var x129: u64 = undefined; + var x130: u64 = undefined; + mulxU64(&x129, &x130, x3, (arg1[3])); + var x131: u64 = undefined; + var x132: u64 = undefined; + mulxU64(&x131, &x132, x3, (arg1[2])); + var x133: u64 = undefined; + var x134: u64 = undefined; + mulxU64(&x133, &x134, x3, (arg1[1])); + var x135: u64 = undefined; + var x136: u64 = undefined; + mulxU64(&x135, &x136, x3, (arg1[0])); + var x137: u64 = undefined; + var x138: u1 = undefined; + addcarryxU64(&x137, &x138, 0x0, x136, x133); + var x139: u64 = undefined; + var x140: u1 = undefined; + addcarryxU64(&x139, &x140, x138, x134, x131); + var x141: u64 = undefined; + var x142: u1 = undefined; + addcarryxU64(&x141, &x142, x140, x132, x129); + const x143 = (cast(u64, x142) + x130); + var x144: u64 = undefined; + var x145: u1 = undefined; + addcarryxU64(&x144, &x145, 0x0, x120, x135); + var x146: u64 = undefined; + var x147: u1 = undefined; + addcarryxU64(&x146, &x147, x145, x122, x137); + var x148: u64 = undefined; + var x149: u1 = undefined; + addcarryxU64(&x148, &x149, x147, x124, x139); + var x150: u64 = undefined; + var x151: u1 = undefined; + addcarryxU64(&x150, &x151, x149, x126, x141); + var x152: u64 = undefined; + var x153: u1 = undefined; + addcarryxU64(&x152, &x153, x151, x128, x143); + var x154: u64 = undefined; + var x155: u64 = undefined; + mulxU64(&x154, &x155, x144, 0xffffffff00000001); + var x156: u64 = undefined; + var x157: u64 = undefined; + mulxU64(&x156, &x157, x144, 0xffffffff); + var x158: u64 = undefined; + var x159: u64 = undefined; + mulxU64(&x158, &x159, x144, 0xffffffffffffffff); + var x160: u64 = undefined; + var x161: u1 = undefined; + addcarryxU64(&x160, &x161, 0x0, x159, x156); + const x162 = (cast(u64, x161) + x157); + var x163: u64 = undefined; + var x164: u1 = undefined; + addcarryxU64(&x163, &x164, 0x0, x144, x158); + var x165: u64 = undefined; + var x166: u1 = undefined; + addcarryxU64(&x165, &x166, x164, x146, x160); + var x167: u64 = undefined; + var x168: u1 = undefined; + addcarryxU64(&x167, &x168, x166, x148, x162); + var x169: u64 = undefined; + var x170: u1 = undefined; + addcarryxU64(&x169, &x170, x168, x150, x154); + var x171: u64 = undefined; + var x172: u1 = undefined; + addcarryxU64(&x171, &x172, x170, x152, x155); + const x173 = (cast(u64, x172) + cast(u64, x153)); + var x174: u64 = undefined; + var x175: u1 = undefined; + subborrowxU64(&x174, &x175, 0x0, x165, 0xffffffffffffffff); + var x176: u64 = undefined; + var x177: u1 = undefined; + subborrowxU64(&x176, &x177, x175, x167, 0xffffffff); + var x178: u64 = undefined; + var x179: u1 = undefined; + subborrowxU64(&x178, &x179, x177, x169, cast(u64, 0x0)); + var x180: u64 = undefined; + var x181: u1 = undefined; + subborrowxU64(&x180, &x181, x179, x171, 0xffffffff00000001); + var x182: u64 = undefined; + var x183: u1 = undefined; + subborrowxU64(&x182, &x183, x181, x173, cast(u64, 0x0)); + var x184: u64 = undefined; + cmovznzU64(&x184, x183, x174, x165); + var x185: u64 = undefined; + cmovznzU64(&x185, x183, x176, x167); + var x186: u64 = undefined; + cmovznzU64(&x186, x183, x178, x169); + var x187: u64 = undefined; + cmovznzU64(&x187, x183, x180, x171); + out1[0] = x184; + out1[1] = x185; + out1[2] = x186; + out1[3] = x187; +} + +/// The function add adds two field elements in the Montgomery domain. +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// 0 ≤ eval arg2 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) + eval (from_montgomery arg2)) mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn add(out1: *[4]u64, arg1: [4]u64, arg2: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + addcarryxU64(&x1, &x2, 0x0, (arg1[0]), (arg2[0])); + var x3: u64 = undefined; + var x4: u1 = undefined; + addcarryxU64(&x3, &x4, x2, (arg1[1]), (arg2[1])); + var x5: u64 = undefined; + var x6: u1 = undefined; + addcarryxU64(&x5, &x6, x4, (arg1[2]), (arg2[2])); + var x7: u64 = undefined; + var x8: u1 = undefined; + addcarryxU64(&x7, &x8, x6, (arg1[3]), (arg2[3])); + var x9: u64 = undefined; + var x10: u1 = undefined; + subborrowxU64(&x9, &x10, 0x0, x1, 0xffffffffffffffff); + var x11: u64 = undefined; + var x12: u1 = undefined; + subborrowxU64(&x11, &x12, x10, x3, 0xffffffff); + var x13: u64 = undefined; + var x14: u1 = undefined; + subborrowxU64(&x13, &x14, x12, x5, cast(u64, 0x0)); + var x15: u64 = undefined; + var x16: u1 = undefined; + subborrowxU64(&x15, &x16, x14, x7, 0xffffffff00000001); + var x17: u64 = undefined; + var x18: u1 = undefined; + subborrowxU64(&x17, &x18, x16, cast(u64, x8), cast(u64, 0x0)); + var x19: u64 = undefined; + cmovznzU64(&x19, x18, x9, x1); + var x20: u64 = undefined; + cmovznzU64(&x20, x18, x11, x3); + var x21: u64 = undefined; + cmovznzU64(&x21, x18, x13, x5); + var x22: u64 = undefined; + cmovznzU64(&x22, x18, x15, x7); + out1[0] = x19; + out1[1] = x20; + out1[2] = x21; + out1[3] = x22; +} + +/// The function sub subtracts two field elements in the Montgomery domain. +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// 0 ≤ eval arg2 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) - eval (from_montgomery arg2)) mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn sub(out1: *[4]u64, arg1: [4]u64, arg2: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + subborrowxU64(&x1, &x2, 0x0, (arg1[0]), (arg2[0])); + var x3: u64 = undefined; + var x4: u1 = undefined; + subborrowxU64(&x3, &x4, x2, (arg1[1]), (arg2[1])); + var x5: u64 = undefined; + var x6: u1 = undefined; + subborrowxU64(&x5, &x6, x4, (arg1[2]), (arg2[2])); + var x7: u64 = undefined; + var x8: u1 = undefined; + subborrowxU64(&x7, &x8, x6, (arg1[3]), (arg2[3])); + var x9: u64 = undefined; + cmovznzU64(&x9, x8, cast(u64, 0x0), 0xffffffffffffffff); + var x10: u64 = undefined; + var x11: u1 = undefined; + addcarryxU64(&x10, &x11, 0x0, x1, x9); + var x12: u64 = undefined; + var x13: u1 = undefined; + addcarryxU64(&x12, &x13, x11, x3, (x9 & 0xffffffff)); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, x13, x5, cast(u64, 0x0)); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, x15, x7, (x9 & 0xffffffff00000001)); + out1[0] = x10; + out1[1] = x12; + out1[2] = x14; + out1[3] = x16; +} + +/// The function opp negates a field element in the Montgomery domain. +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = -eval (from_montgomery arg1) mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn opp(out1: *[4]u64, arg1: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + subborrowxU64(&x1, &x2, 0x0, cast(u64, 0x0), (arg1[0])); + var x3: u64 = undefined; + var x4: u1 = undefined; + subborrowxU64(&x3, &x4, x2, cast(u64, 0x0), (arg1[1])); + var x5: u64 = undefined; + var x6: u1 = undefined; + subborrowxU64(&x5, &x6, x4, cast(u64, 0x0), (arg1[2])); + var x7: u64 = undefined; + var x8: u1 = undefined; + subborrowxU64(&x7, &x8, x6, cast(u64, 0x0), (arg1[3])); + var x9: u64 = undefined; + cmovznzU64(&x9, x8, cast(u64, 0x0), 0xffffffffffffffff); + var x10: u64 = undefined; + var x11: u1 = undefined; + addcarryxU64(&x10, &x11, 0x0, x1, x9); + var x12: u64 = undefined; + var x13: u1 = undefined; + addcarryxU64(&x12, &x13, x11, x3, (x9 & 0xffffffff)); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, x13, x5, cast(u64, 0x0)); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, x15, x7, (x9 & 0xffffffff00000001)); + out1[0] = x10; + out1[1] = x12; + out1[2] = x14; + out1[3] = x16; +} + +/// The function fromMontgomery translates a field element out of the Montgomery domain. +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval out1 mod m = (eval arg1 * ((2^64)⁻¹ mod m)^4) mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn fromMontgomery(out1: *[4]u64, arg1: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[0]); + var x2: u64 = undefined; + var x3: u64 = undefined; + mulxU64(&x2, &x3, x1, 0xffffffff00000001); + var x4: u64 = undefined; + var x5: u64 = undefined; + mulxU64(&x4, &x5, x1, 0xffffffff); + var x6: u64 = undefined; + var x7: u64 = undefined; + mulxU64(&x6, &x7, x1, 0xffffffffffffffff); + var x8: u64 = undefined; + var x9: u1 = undefined; + addcarryxU64(&x8, &x9, 0x0, x7, x4); + var x10: u64 = undefined; + var x11: u1 = undefined; + addcarryxU64(&x10, &x11, 0x0, x1, x6); + var x12: u64 = undefined; + var x13: u1 = undefined; + addcarryxU64(&x12, &x13, x11, cast(u64, 0x0), x8); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, 0x0, x12, (arg1[1])); + var x16: u64 = undefined; + var x17: u64 = undefined; + mulxU64(&x16, &x17, x14, 0xffffffff00000001); + var x18: u64 = undefined; + var x19: u64 = undefined; + mulxU64(&x18, &x19, x14, 0xffffffff); + var x20: u64 = undefined; + var x21: u64 = undefined; + mulxU64(&x20, &x21, x14, 0xffffffffffffffff); + var x22: u64 = undefined; + var x23: u1 = undefined; + addcarryxU64(&x22, &x23, 0x0, x21, x18); + var x24: u64 = undefined; + var x25: u1 = undefined; + addcarryxU64(&x24, &x25, 0x0, x14, x20); + var x26: u64 = undefined; + var x27: u1 = undefined; + addcarryxU64(&x26, &x27, x25, (cast(u64, x15) + (cast(u64, x13) + (cast(u64, x9) + x5))), x22); + var x28: u64 = undefined; + var x29: u1 = undefined; + addcarryxU64(&x28, &x29, x27, x2, (cast(u64, x23) + x19)); + var x30: u64 = undefined; + var x31: u1 = undefined; + addcarryxU64(&x30, &x31, x29, x3, x16); + var x32: u64 = undefined; + var x33: u1 = undefined; + addcarryxU64(&x32, &x33, 0x0, x26, (arg1[2])); + var x34: u64 = undefined; + var x35: u1 = undefined; + addcarryxU64(&x34, &x35, x33, x28, cast(u64, 0x0)); + var x36: u64 = undefined; + var x37: u1 = undefined; + addcarryxU64(&x36, &x37, x35, x30, cast(u64, 0x0)); + var x38: u64 = undefined; + var x39: u64 = undefined; + mulxU64(&x38, &x39, x32, 0xffffffff00000001); + var x40: u64 = undefined; + var x41: u64 = undefined; + mulxU64(&x40, &x41, x32, 0xffffffff); + var x42: u64 = undefined; + var x43: u64 = undefined; + mulxU64(&x42, &x43, x32, 0xffffffffffffffff); + var x44: u64 = undefined; + var x45: u1 = undefined; + addcarryxU64(&x44, &x45, 0x0, x43, x40); + var x46: u64 = undefined; + var x47: u1 = undefined; + addcarryxU64(&x46, &x47, 0x0, x32, x42); + var x48: u64 = undefined; + var x49: u1 = undefined; + addcarryxU64(&x48, &x49, x47, x34, x44); + var x50: u64 = undefined; + var x51: u1 = undefined; + addcarryxU64(&x50, &x51, x49, x36, (cast(u64, x45) + x41)); + var x52: u64 = undefined; + var x53: u1 = undefined; + addcarryxU64(&x52, &x53, x51, (cast(u64, x37) + (cast(u64, x31) + x17)), x38); + var x54: u64 = undefined; + var x55: u1 = undefined; + addcarryxU64(&x54, &x55, 0x0, x48, (arg1[3])); + var x56: u64 = undefined; + var x57: u1 = undefined; + addcarryxU64(&x56, &x57, x55, x50, cast(u64, 0x0)); + var x58: u64 = undefined; + var x59: u1 = undefined; + addcarryxU64(&x58, &x59, x57, x52, cast(u64, 0x0)); + var x60: u64 = undefined; + var x61: u64 = undefined; + mulxU64(&x60, &x61, x54, 0xffffffff00000001); + var x62: u64 = undefined; + var x63: u64 = undefined; + mulxU64(&x62, &x63, x54, 0xffffffff); + var x64: u64 = undefined; + var x65: u64 = undefined; + mulxU64(&x64, &x65, x54, 0xffffffffffffffff); + var x66: u64 = undefined; + var x67: u1 = undefined; + addcarryxU64(&x66, &x67, 0x0, x65, x62); + var x68: u64 = undefined; + var x69: u1 = undefined; + addcarryxU64(&x68, &x69, 0x0, x54, x64); + var x70: u64 = undefined; + var x71: u1 = undefined; + addcarryxU64(&x70, &x71, x69, x56, x66); + var x72: u64 = undefined; + var x73: u1 = undefined; + addcarryxU64(&x72, &x73, x71, x58, (cast(u64, x67) + x63)); + var x74: u64 = undefined; + var x75: u1 = undefined; + addcarryxU64(&x74, &x75, x73, (cast(u64, x59) + (cast(u64, x53) + x39)), x60); + const x76 = (cast(u64, x75) + x61); + var x77: u64 = undefined; + var x78: u1 = undefined; + subborrowxU64(&x77, &x78, 0x0, x70, 0xffffffffffffffff); + var x79: u64 = undefined; + var x80: u1 = undefined; + subborrowxU64(&x79, &x80, x78, x72, 0xffffffff); + var x81: u64 = undefined; + var x82: u1 = undefined; + subborrowxU64(&x81, &x82, x80, x74, cast(u64, 0x0)); + var x83: u64 = undefined; + var x84: u1 = undefined; + subborrowxU64(&x83, &x84, x82, x76, 0xffffffff00000001); + var x85: u64 = undefined; + var x86: u1 = undefined; + subborrowxU64(&x85, &x86, x84, cast(u64, 0x0), cast(u64, 0x0)); + var x87: u64 = undefined; + cmovznzU64(&x87, x86, x77, x70); + var x88: u64 = undefined; + cmovznzU64(&x88, x86, x79, x72); + var x89: u64 = undefined; + cmovznzU64(&x89, x86, x81, x74); + var x90: u64 = undefined; + cmovznzU64(&x90, x86, x83, x76); + out1[0] = x87; + out1[1] = x88; + out1[2] = x89; + out1[3] = x90; +} + +/// The function toMontgomery translates a field element into the Montgomery domain. +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = eval arg1 mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn toMontgomery(out1: *[4]u64, arg1: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[1]); + const x2 = (arg1[2]); + const x3 = (arg1[3]); + const x4 = (arg1[0]); + var x5: u64 = undefined; + var x6: u64 = undefined; + mulxU64(&x5, &x6, x4, 0x4fffffffd); + var x7: u64 = undefined; + var x8: u64 = undefined; + mulxU64(&x7, &x8, x4, 0xfffffffffffffffe); + var x9: u64 = undefined; + var x10: u64 = undefined; + mulxU64(&x9, &x10, x4, 0xfffffffbffffffff); + var x11: u64 = undefined; + var x12: u64 = undefined; + mulxU64(&x11, &x12, x4, 0x3); + var x13: u64 = undefined; + var x14: u1 = undefined; + addcarryxU64(&x13, &x14, 0x0, x12, x9); + var x15: u64 = undefined; + var x16: u1 = undefined; + addcarryxU64(&x15, &x16, x14, x10, x7); + var x17: u64 = undefined; + var x18: u1 = undefined; + addcarryxU64(&x17, &x18, x16, x8, x5); + var x19: u64 = undefined; + var x20: u64 = undefined; + mulxU64(&x19, &x20, x11, 0xffffffff00000001); + var x21: u64 = undefined; + var x22: u64 = undefined; + mulxU64(&x21, &x22, x11, 0xffffffff); + var x23: u64 = undefined; + var x24: u64 = undefined; + mulxU64(&x23, &x24, x11, 0xffffffffffffffff); + var x25: u64 = undefined; + var x26: u1 = undefined; + addcarryxU64(&x25, &x26, 0x0, x24, x21); + var x27: u64 = undefined; + var x28: u1 = undefined; + addcarryxU64(&x27, &x28, 0x0, x11, x23); + var x29: u64 = undefined; + var x30: u1 = undefined; + addcarryxU64(&x29, &x30, x28, x13, x25); + var x31: u64 = undefined; + var x32: u1 = undefined; + addcarryxU64(&x31, &x32, x30, x15, (cast(u64, x26) + x22)); + var x33: u64 = undefined; + var x34: u1 = undefined; + addcarryxU64(&x33, &x34, x32, x17, x19); + var x35: u64 = undefined; + var x36: u1 = undefined; + addcarryxU64(&x35, &x36, x34, (cast(u64, x18) + x6), x20); + var x37: u64 = undefined; + var x38: u64 = undefined; + mulxU64(&x37, &x38, x1, 0x4fffffffd); + var x39: u64 = undefined; + var x40: u64 = undefined; + mulxU64(&x39, &x40, x1, 0xfffffffffffffffe); + var x41: u64 = undefined; + var x42: u64 = undefined; + mulxU64(&x41, &x42, x1, 0xfffffffbffffffff); + var x43: u64 = undefined; + var x44: u64 = undefined; + mulxU64(&x43, &x44, x1, 0x3); + var x45: u64 = undefined; + var x46: u1 = undefined; + addcarryxU64(&x45, &x46, 0x0, x44, x41); + var x47: u64 = undefined; + var x48: u1 = undefined; + addcarryxU64(&x47, &x48, x46, x42, x39); + var x49: u64 = undefined; + var x50: u1 = undefined; + addcarryxU64(&x49, &x50, x48, x40, x37); + var x51: u64 = undefined; + var x52: u1 = undefined; + addcarryxU64(&x51, &x52, 0x0, x29, x43); + var x53: u64 = undefined; + var x54: u1 = undefined; + addcarryxU64(&x53, &x54, x52, x31, x45); + var x55: u64 = undefined; + var x56: u1 = undefined; + addcarryxU64(&x55, &x56, x54, x33, x47); + var x57: u64 = undefined; + var x58: u1 = undefined; + addcarryxU64(&x57, &x58, x56, x35, x49); + var x59: u64 = undefined; + var x60: u64 = undefined; + mulxU64(&x59, &x60, x51, 0xffffffff00000001); + var x61: u64 = undefined; + var x62: u64 = undefined; + mulxU64(&x61, &x62, x51, 0xffffffff); + var x63: u64 = undefined; + var x64: u64 = undefined; + mulxU64(&x63, &x64, x51, 0xffffffffffffffff); + var x65: u64 = undefined; + var x66: u1 = undefined; + addcarryxU64(&x65, &x66, 0x0, x64, x61); + var x67: u64 = undefined; + var x68: u1 = undefined; + addcarryxU64(&x67, &x68, 0x0, x51, x63); + var x69: u64 = undefined; + var x70: u1 = undefined; + addcarryxU64(&x69, &x70, x68, x53, x65); + var x71: u64 = undefined; + var x72: u1 = undefined; + addcarryxU64(&x71, &x72, x70, x55, (cast(u64, x66) + x62)); + var x73: u64 = undefined; + var x74: u1 = undefined; + addcarryxU64(&x73, &x74, x72, x57, x59); + var x75: u64 = undefined; + var x76: u1 = undefined; + addcarryxU64(&x75, &x76, x74, ((cast(u64, x58) + cast(u64, x36)) + (cast(u64, x50) + x38)), x60); + var x77: u64 = undefined; + var x78: u64 = undefined; + mulxU64(&x77, &x78, x2, 0x4fffffffd); + var x79: u64 = undefined; + var x80: u64 = undefined; + mulxU64(&x79, &x80, x2, 0xfffffffffffffffe); + var x81: u64 = undefined; + var x82: u64 = undefined; + mulxU64(&x81, &x82, x2, 0xfffffffbffffffff); + var x83: u64 = undefined; + var x84: u64 = undefined; + mulxU64(&x83, &x84, x2, 0x3); + var x85: u64 = undefined; + var x86: u1 = undefined; + addcarryxU64(&x85, &x86, 0x0, x84, x81); + var x87: u64 = undefined; + var x88: u1 = undefined; + addcarryxU64(&x87, &x88, x86, x82, x79); + var x89: u64 = undefined; + var x90: u1 = undefined; + addcarryxU64(&x89, &x90, x88, x80, x77); + var x91: u64 = undefined; + var x92: u1 = undefined; + addcarryxU64(&x91, &x92, 0x0, x69, x83); + var x93: u64 = undefined; + var x94: u1 = undefined; + addcarryxU64(&x93, &x94, x92, x71, x85); + var x95: u64 = undefined; + var x96: u1 = undefined; + addcarryxU64(&x95, &x96, x94, x73, x87); + var x97: u64 = undefined; + var x98: u1 = undefined; + addcarryxU64(&x97, &x98, x96, x75, x89); + var x99: u64 = undefined; + var x100: u64 = undefined; + mulxU64(&x99, &x100, x91, 0xffffffff00000001); + var x101: u64 = undefined; + var x102: u64 = undefined; + mulxU64(&x101, &x102, x91, 0xffffffff); + var x103: u64 = undefined; + var x104: u64 = undefined; + mulxU64(&x103, &x104, x91, 0xffffffffffffffff); + var x105: u64 = undefined; + var x106: u1 = undefined; + addcarryxU64(&x105, &x106, 0x0, x104, x101); + var x107: u64 = undefined; + var x108: u1 = undefined; + addcarryxU64(&x107, &x108, 0x0, x91, x103); + var x109: u64 = undefined; + var x110: u1 = undefined; + addcarryxU64(&x109, &x110, x108, x93, x105); + var x111: u64 = undefined; + var x112: u1 = undefined; + addcarryxU64(&x111, &x112, x110, x95, (cast(u64, x106) + x102)); + var x113: u64 = undefined; + var x114: u1 = undefined; + addcarryxU64(&x113, &x114, x112, x97, x99); + var x115: u64 = undefined; + var x116: u1 = undefined; + addcarryxU64(&x115, &x116, x114, ((cast(u64, x98) + cast(u64, x76)) + (cast(u64, x90) + x78)), x100); + var x117: u64 = undefined; + var x118: u64 = undefined; + mulxU64(&x117, &x118, x3, 0x4fffffffd); + var x119: u64 = undefined; + var x120: u64 = undefined; + mulxU64(&x119, &x120, x3, 0xfffffffffffffffe); + var x121: u64 = undefined; + var x122: u64 = undefined; + mulxU64(&x121, &x122, x3, 0xfffffffbffffffff); + var x123: u64 = undefined; + var x124: u64 = undefined; + mulxU64(&x123, &x124, x3, 0x3); + var x125: u64 = undefined; + var x126: u1 = undefined; + addcarryxU64(&x125, &x126, 0x0, x124, x121); + var x127: u64 = undefined; + var x128: u1 = undefined; + addcarryxU64(&x127, &x128, x126, x122, x119); + var x129: u64 = undefined; + var x130: u1 = undefined; + addcarryxU64(&x129, &x130, x128, x120, x117); + var x131: u64 = undefined; + var x132: u1 = undefined; + addcarryxU64(&x131, &x132, 0x0, x109, x123); + var x133: u64 = undefined; + var x134: u1 = undefined; + addcarryxU64(&x133, &x134, x132, x111, x125); + var x135: u64 = undefined; + var x136: u1 = undefined; + addcarryxU64(&x135, &x136, x134, x113, x127); + var x137: u64 = undefined; + var x138: u1 = undefined; + addcarryxU64(&x137, &x138, x136, x115, x129); + var x139: u64 = undefined; + var x140: u64 = undefined; + mulxU64(&x139, &x140, x131, 0xffffffff00000001); + var x141: u64 = undefined; + var x142: u64 = undefined; + mulxU64(&x141, &x142, x131, 0xffffffff); + var x143: u64 = undefined; + var x144: u64 = undefined; + mulxU64(&x143, &x144, x131, 0xffffffffffffffff); + var x145: u64 = undefined; + var x146: u1 = undefined; + addcarryxU64(&x145, &x146, 0x0, x144, x141); + var x147: u64 = undefined; + var x148: u1 = undefined; + addcarryxU64(&x147, &x148, 0x0, x131, x143); + var x149: u64 = undefined; + var x150: u1 = undefined; + addcarryxU64(&x149, &x150, x148, x133, x145); + var x151: u64 = undefined; + var x152: u1 = undefined; + addcarryxU64(&x151, &x152, x150, x135, (cast(u64, x146) + x142)); + var x153: u64 = undefined; + var x154: u1 = undefined; + addcarryxU64(&x153, &x154, x152, x137, x139); + var x155: u64 = undefined; + var x156: u1 = undefined; + addcarryxU64(&x155, &x156, x154, ((cast(u64, x138) + cast(u64, x116)) + (cast(u64, x130) + x118)), x140); + var x157: u64 = undefined; + var x158: u1 = undefined; + subborrowxU64(&x157, &x158, 0x0, x149, 0xffffffffffffffff); + var x159: u64 = undefined; + var x160: u1 = undefined; + subborrowxU64(&x159, &x160, x158, x151, 0xffffffff); + var x161: u64 = undefined; + var x162: u1 = undefined; + subborrowxU64(&x161, &x162, x160, x153, cast(u64, 0x0)); + var x163: u64 = undefined; + var x164: u1 = undefined; + subborrowxU64(&x163, &x164, x162, x155, 0xffffffff00000001); + var x165: u64 = undefined; + var x166: u1 = undefined; + subborrowxU64(&x165, &x166, x164, cast(u64, x156), cast(u64, 0x0)); + var x167: u64 = undefined; + cmovznzU64(&x167, x166, x157, x149); + var x168: u64 = undefined; + cmovznzU64(&x168, x166, x159, x151); + var x169: u64 = undefined; + cmovznzU64(&x169, x166, x161, x153); + var x170: u64 = undefined; + cmovznzU64(&x170, x166, x163, x155); + out1[0] = x167; + out1[1] = x168; + out1[2] = x169; + out1[3] = x170; +} + +/// The function nonzero outputs a single non-zero word if the input is non-zero and zero otherwise. +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// out1 = 0 ↔ eval (from_montgomery arg1) mod m = 0 +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +pub fn nonzero(out1: *u64, arg1: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = ((arg1[0]) | ((arg1[1]) | ((arg1[2]) | (arg1[3])))); + out1.* = x1; +} + +/// The function selectznz is a multi-limb conditional select. +/// Postconditions: +/// eval out1 = (if arg1 = 0 then eval arg2 else eval arg3) +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn selectznz(out1: *[4]u64, arg1: u1, arg2: [4]u64, arg3: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + cmovznzU64(&x1, arg1, (arg2[0]), (arg3[0])); + var x2: u64 = undefined; + cmovznzU64(&x2, arg1, (arg2[1]), (arg3[1])); + var x3: u64 = undefined; + cmovznzU64(&x3, arg1, (arg2[2]), (arg3[2])); + var x4: u64 = undefined; + cmovznzU64(&x4, arg1, (arg2[3]), (arg3[3])); + out1[0] = x1; + out1[1] = x2; + out1[2] = x3; + out1[3] = x4; +} + +/// The function toBytes serializes a field element NOT in the Montgomery domain to bytes in little-endian order. +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// out1 = map (λ x, ⌊((eval arg1 mod m) mod 2^(8 * (x + 1))) / 2^(8 * x)⌋) [0..31] +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff]] +pub fn toBytes(out1: *[32]u8, arg1: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[3]); + const x2 = (arg1[2]); + const x3 = (arg1[1]); + const x4 = (arg1[0]); + const x5 = cast(u8, (x4 & cast(u64, 0xff))); + const x6 = (x4 >> 8); + const x7 = cast(u8, (x6 & cast(u64, 0xff))); + const x8 = (x6 >> 8); + const x9 = cast(u8, (x8 & cast(u64, 0xff))); + const x10 = (x8 >> 8); + const x11 = cast(u8, (x10 & cast(u64, 0xff))); + const x12 = (x10 >> 8); + const x13 = cast(u8, (x12 & cast(u64, 0xff))); + const x14 = (x12 >> 8); + const x15 = cast(u8, (x14 & cast(u64, 0xff))); + const x16 = (x14 >> 8); + const x17 = cast(u8, (x16 & cast(u64, 0xff))); + const x18 = cast(u8, (x16 >> 8)); + const x19 = cast(u8, (x3 & cast(u64, 0xff))); + const x20 = (x3 >> 8); + const x21 = cast(u8, (x20 & cast(u64, 0xff))); + const x22 = (x20 >> 8); + const x23 = cast(u8, (x22 & cast(u64, 0xff))); + const x24 = (x22 >> 8); + const x25 = cast(u8, (x24 & cast(u64, 0xff))); + const x26 = (x24 >> 8); + const x27 = cast(u8, (x26 & cast(u64, 0xff))); + const x28 = (x26 >> 8); + const x29 = cast(u8, (x28 & cast(u64, 0xff))); + const x30 = (x28 >> 8); + const x31 = cast(u8, (x30 & cast(u64, 0xff))); + const x32 = cast(u8, (x30 >> 8)); + const x33 = cast(u8, (x2 & cast(u64, 0xff))); + const x34 = (x2 >> 8); + const x35 = cast(u8, (x34 & cast(u64, 0xff))); + const x36 = (x34 >> 8); + const x37 = cast(u8, (x36 & cast(u64, 0xff))); + const x38 = (x36 >> 8); + const x39 = cast(u8, (x38 & cast(u64, 0xff))); + const x40 = (x38 >> 8); + const x41 = cast(u8, (x40 & cast(u64, 0xff))); + const x42 = (x40 >> 8); + const x43 = cast(u8, (x42 & cast(u64, 0xff))); + const x44 = (x42 >> 8); + const x45 = cast(u8, (x44 & cast(u64, 0xff))); + const x46 = cast(u8, (x44 >> 8)); + const x47 = cast(u8, (x1 & cast(u64, 0xff))); + const x48 = (x1 >> 8); + const x49 = cast(u8, (x48 & cast(u64, 0xff))); + const x50 = (x48 >> 8); + const x51 = cast(u8, (x50 & cast(u64, 0xff))); + const x52 = (x50 >> 8); + const x53 = cast(u8, (x52 & cast(u64, 0xff))); + const x54 = (x52 >> 8); + const x55 = cast(u8, (x54 & cast(u64, 0xff))); + const x56 = (x54 >> 8); + const x57 = cast(u8, (x56 & cast(u64, 0xff))); + const x58 = (x56 >> 8); + const x59 = cast(u8, (x58 & cast(u64, 0xff))); + const x60 = cast(u8, (x58 >> 8)); + out1[0] = x5; + out1[1] = x7; + out1[2] = x9; + out1[3] = x11; + out1[4] = x13; + out1[5] = x15; + out1[6] = x17; + out1[7] = x18; + out1[8] = x19; + out1[9] = x21; + out1[10] = x23; + out1[11] = x25; + out1[12] = x27; + out1[13] = x29; + out1[14] = x31; + out1[15] = x32; + out1[16] = x33; + out1[17] = x35; + out1[18] = x37; + out1[19] = x39; + out1[20] = x41; + out1[21] = x43; + out1[22] = x45; + out1[23] = x46; + out1[24] = x47; + out1[25] = x49; + out1[26] = x51; + out1[27] = x53; + out1[28] = x55; + out1[29] = x57; + out1[30] = x59; + out1[31] = x60; +} + +/// The function fromBytes deserializes a field element NOT in the Montgomery domain from bytes in little-endian order. +/// Preconditions: +/// 0 ≤ bytes_eval arg1 < m +/// Postconditions: +/// eval out1 mod m = bytes_eval arg1 mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn fromBytes(out1: *[4]u64, arg1: [32]u8) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (cast(u64, (arg1[31])) << 56); + const x2 = (cast(u64, (arg1[30])) << 48); + const x3 = (cast(u64, (arg1[29])) << 40); + const x4 = (cast(u64, (arg1[28])) << 32); + const x5 = (cast(u64, (arg1[27])) << 24); + const x6 = (cast(u64, (arg1[26])) << 16); + const x7 = (cast(u64, (arg1[25])) << 8); + const x8 = (arg1[24]); + const x9 = (cast(u64, (arg1[23])) << 56); + const x10 = (cast(u64, (arg1[22])) << 48); + const x11 = (cast(u64, (arg1[21])) << 40); + const x12 = (cast(u64, (arg1[20])) << 32); + const x13 = (cast(u64, (arg1[19])) << 24); + const x14 = (cast(u64, (arg1[18])) << 16); + const x15 = (cast(u64, (arg1[17])) << 8); + const x16 = (arg1[16]); + const x17 = (cast(u64, (arg1[15])) << 56); + const x18 = (cast(u64, (arg1[14])) << 48); + const x19 = (cast(u64, (arg1[13])) << 40); + const x20 = (cast(u64, (arg1[12])) << 32); + const x21 = (cast(u64, (arg1[11])) << 24); + const x22 = (cast(u64, (arg1[10])) << 16); + const x23 = (cast(u64, (arg1[9])) << 8); + const x24 = (arg1[8]); + const x25 = (cast(u64, (arg1[7])) << 56); + const x26 = (cast(u64, (arg1[6])) << 48); + const x27 = (cast(u64, (arg1[5])) << 40); + const x28 = (cast(u64, (arg1[4])) << 32); + const x29 = (cast(u64, (arg1[3])) << 24); + const x30 = (cast(u64, (arg1[2])) << 16); + const x31 = (cast(u64, (arg1[1])) << 8); + const x32 = (arg1[0]); + const x33 = (x31 + cast(u64, x32)); + const x34 = (x30 + x33); + const x35 = (x29 + x34); + const x36 = (x28 + x35); + const x37 = (x27 + x36); + const x38 = (x26 + x37); + const x39 = (x25 + x38); + const x40 = (x23 + cast(u64, x24)); + const x41 = (x22 + x40); + const x42 = (x21 + x41); + const x43 = (x20 + x42); + const x44 = (x19 + x43); + const x45 = (x18 + x44); + const x46 = (x17 + x45); + const x47 = (x15 + cast(u64, x16)); + const x48 = (x14 + x47); + const x49 = (x13 + x48); + const x50 = (x12 + x49); + const x51 = (x11 + x50); + const x52 = (x10 + x51); + const x53 = (x9 + x52); + const x54 = (x7 + cast(u64, x8)); + const x55 = (x6 + x54); + const x56 = (x5 + x55); + const x57 = (x4 + x56); + const x58 = (x3 + x57); + const x59 = (x2 + x58); + const x60 = (x1 + x59); + out1[0] = x39; + out1[1] = x46; + out1[2] = x53; + out1[3] = x60; +} + +/// The function setOne returns the field element one in the Montgomery domain. +/// Postconditions: +/// eval (from_montgomery out1) mod m = 1 mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn setOne(out1: *[4]u64) void { + @setRuntimeSafety(mode == .Debug); + + out1[0] = cast(u64, 0x1); + out1[1] = 0xffffffff00000000; + out1[2] = 0xffffffffffffffff; + out1[3] = 0xfffffffe; +} + +/// The function msat returns the saturated representation of the prime modulus. +/// Postconditions: +/// twos_complement_eval out1 = m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn msat(out1: *[5]u64) void { + @setRuntimeSafety(mode == .Debug); + + out1[0] = 0xffffffffffffffff; + out1[1] = 0xffffffff; + out1[2] = cast(u64, 0x0); + out1[3] = 0xffffffff00000001; + out1[4] = cast(u64, 0x0); +} + +/// The function divstep computes a divstep. +/// Preconditions: +/// 0 ≤ eval arg4 < m +/// 0 ≤ eval arg5 < m +/// Postconditions: +/// out1 = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then 1 - arg1 else 1 + arg1) +/// twos_complement_eval out2 = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then twos_complement_eval arg3 else twos_complement_eval arg2) +/// twos_complement_eval out3 = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then ⌊(twos_complement_eval arg3 - twos_complement_eval arg2) / 2⌋ else ⌊(twos_complement_eval arg3 + (twos_complement_eval arg3 mod 2) * twos_complement_eval arg2) / 2⌋) +/// eval (from_montgomery out4) mod m = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then (2 * eval (from_montgomery arg5)) mod m else (2 * eval (from_montgomery arg4)) mod m) +/// eval (from_montgomery out5) mod m = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then (eval (from_montgomery arg4) - eval (from_montgomery arg4)) mod m else (eval (from_montgomery arg5) + (twos_complement_eval arg3 mod 2) * eval (from_montgomery arg4)) mod m) +/// 0 ≤ eval out5 < m +/// 0 ≤ eval out5 < m +/// 0 ≤ eval out2 < m +/// 0 ≤ eval out3 < m +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0xffffffffffffffff] +/// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg4: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg5: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// out3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// out4: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// out5: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn divstep(out1: *u64, out2: *[5]u64, out3: *[5]u64, out4: *[4]u64, out5: *[4]u64, arg1: u64, arg2: [5]u64, arg3: [5]u64, arg4: [4]u64, arg5: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + addcarryxU64(&x1, &x2, 0x0, (~arg1), cast(u64, 0x1)); + const x3 = (cast(u1, (x1 >> 63)) & cast(u1, ((arg3[0]) & cast(u64, 0x1)))); + var x4: u64 = undefined; + var x5: u1 = undefined; + addcarryxU64(&x4, &x5, 0x0, (~arg1), cast(u64, 0x1)); + var x6: u64 = undefined; + cmovznzU64(&x6, x3, arg1, x4); + var x7: u64 = undefined; + cmovznzU64(&x7, x3, (arg2[0]), (arg3[0])); + var x8: u64 = undefined; + cmovznzU64(&x8, x3, (arg2[1]), (arg3[1])); + var x9: u64 = undefined; + cmovznzU64(&x9, x3, (arg2[2]), (arg3[2])); + var x10: u64 = undefined; + cmovznzU64(&x10, x3, (arg2[3]), (arg3[3])); + var x11: u64 = undefined; + cmovznzU64(&x11, x3, (arg2[4]), (arg3[4])); + var x12: u64 = undefined; + var x13: u1 = undefined; + addcarryxU64(&x12, &x13, 0x0, cast(u64, 0x1), (~(arg2[0]))); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, x13, cast(u64, 0x0), (~(arg2[1]))); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, x15, cast(u64, 0x0), (~(arg2[2]))); + var x18: u64 = undefined; + var x19: u1 = undefined; + addcarryxU64(&x18, &x19, x17, cast(u64, 0x0), (~(arg2[3]))); + var x20: u64 = undefined; + var x21: u1 = undefined; + addcarryxU64(&x20, &x21, x19, cast(u64, 0x0), (~(arg2[4]))); + var x22: u64 = undefined; + cmovznzU64(&x22, x3, (arg3[0]), x12); + var x23: u64 = undefined; + cmovznzU64(&x23, x3, (arg3[1]), x14); + var x24: u64 = undefined; + cmovznzU64(&x24, x3, (arg3[2]), x16); + var x25: u64 = undefined; + cmovznzU64(&x25, x3, (arg3[3]), x18); + var x26: u64 = undefined; + cmovznzU64(&x26, x3, (arg3[4]), x20); + var x27: u64 = undefined; + cmovznzU64(&x27, x3, (arg4[0]), (arg5[0])); + var x28: u64 = undefined; + cmovznzU64(&x28, x3, (arg4[1]), (arg5[1])); + var x29: u64 = undefined; + cmovznzU64(&x29, x3, (arg4[2]), (arg5[2])); + var x30: u64 = undefined; + cmovznzU64(&x30, x3, (arg4[3]), (arg5[3])); + var x31: u64 = undefined; + var x32: u1 = undefined; + addcarryxU64(&x31, &x32, 0x0, x27, x27); + var x33: u64 = undefined; + var x34: u1 = undefined; + addcarryxU64(&x33, &x34, x32, x28, x28); + var x35: u64 = undefined; + var x36: u1 = undefined; + addcarryxU64(&x35, &x36, x34, x29, x29); + var x37: u64 = undefined; + var x38: u1 = undefined; + addcarryxU64(&x37, &x38, x36, x30, x30); + var x39: u64 = undefined; + var x40: u1 = undefined; + subborrowxU64(&x39, &x40, 0x0, x31, 0xffffffffffffffff); + var x41: u64 = undefined; + var x42: u1 = undefined; + subborrowxU64(&x41, &x42, x40, x33, 0xffffffff); + var x43: u64 = undefined; + var x44: u1 = undefined; + subborrowxU64(&x43, &x44, x42, x35, cast(u64, 0x0)); + var x45: u64 = undefined; + var x46: u1 = undefined; + subborrowxU64(&x45, &x46, x44, x37, 0xffffffff00000001); + var x47: u64 = undefined; + var x48: u1 = undefined; + subborrowxU64(&x47, &x48, x46, cast(u64, x38), cast(u64, 0x0)); + const x49 = (arg4[3]); + const x50 = (arg4[2]); + const x51 = (arg4[1]); + const x52 = (arg4[0]); + var x53: u64 = undefined; + var x54: u1 = undefined; + subborrowxU64(&x53, &x54, 0x0, cast(u64, 0x0), x52); + var x55: u64 = undefined; + var x56: u1 = undefined; + subborrowxU64(&x55, &x56, x54, cast(u64, 0x0), x51); + var x57: u64 = undefined; + var x58: u1 = undefined; + subborrowxU64(&x57, &x58, x56, cast(u64, 0x0), x50); + var x59: u64 = undefined; + var x60: u1 = undefined; + subborrowxU64(&x59, &x60, x58, cast(u64, 0x0), x49); + var x61: u64 = undefined; + cmovznzU64(&x61, x60, cast(u64, 0x0), 0xffffffffffffffff); + var x62: u64 = undefined; + var x63: u1 = undefined; + addcarryxU64(&x62, &x63, 0x0, x53, x61); + var x64: u64 = undefined; + var x65: u1 = undefined; + addcarryxU64(&x64, &x65, x63, x55, (x61 & 0xffffffff)); + var x66: u64 = undefined; + var x67: u1 = undefined; + addcarryxU64(&x66, &x67, x65, x57, cast(u64, 0x0)); + var x68: u64 = undefined; + var x69: u1 = undefined; + addcarryxU64(&x68, &x69, x67, x59, (x61 & 0xffffffff00000001)); + var x70: u64 = undefined; + cmovznzU64(&x70, x3, (arg5[0]), x62); + var x71: u64 = undefined; + cmovznzU64(&x71, x3, (arg5[1]), x64); + var x72: u64 = undefined; + cmovznzU64(&x72, x3, (arg5[2]), x66); + var x73: u64 = undefined; + cmovznzU64(&x73, x3, (arg5[3]), x68); + const x74 = cast(u1, (x22 & cast(u64, 0x1))); + var x75: u64 = undefined; + cmovznzU64(&x75, x74, cast(u64, 0x0), x7); + var x76: u64 = undefined; + cmovznzU64(&x76, x74, cast(u64, 0x0), x8); + var x77: u64 = undefined; + cmovznzU64(&x77, x74, cast(u64, 0x0), x9); + var x78: u64 = undefined; + cmovznzU64(&x78, x74, cast(u64, 0x0), x10); + var x79: u64 = undefined; + cmovznzU64(&x79, x74, cast(u64, 0x0), x11); + var x80: u64 = undefined; + var x81: u1 = undefined; + addcarryxU64(&x80, &x81, 0x0, x22, x75); + var x82: u64 = undefined; + var x83: u1 = undefined; + addcarryxU64(&x82, &x83, x81, x23, x76); + var x84: u64 = undefined; + var x85: u1 = undefined; + addcarryxU64(&x84, &x85, x83, x24, x77); + var x86: u64 = undefined; + var x87: u1 = undefined; + addcarryxU64(&x86, &x87, x85, x25, x78); + var x88: u64 = undefined; + var x89: u1 = undefined; + addcarryxU64(&x88, &x89, x87, x26, x79); + var x90: u64 = undefined; + cmovznzU64(&x90, x74, cast(u64, 0x0), x27); + var x91: u64 = undefined; + cmovznzU64(&x91, x74, cast(u64, 0x0), x28); + var x92: u64 = undefined; + cmovznzU64(&x92, x74, cast(u64, 0x0), x29); + var x93: u64 = undefined; + cmovznzU64(&x93, x74, cast(u64, 0x0), x30); + var x94: u64 = undefined; + var x95: u1 = undefined; + addcarryxU64(&x94, &x95, 0x0, x70, x90); + var x96: u64 = undefined; + var x97: u1 = undefined; + addcarryxU64(&x96, &x97, x95, x71, x91); + var x98: u64 = undefined; + var x99: u1 = undefined; + addcarryxU64(&x98, &x99, x97, x72, x92); + var x100: u64 = undefined; + var x101: u1 = undefined; + addcarryxU64(&x100, &x101, x99, x73, x93); + var x102: u64 = undefined; + var x103: u1 = undefined; + subborrowxU64(&x102, &x103, 0x0, x94, 0xffffffffffffffff); + var x104: u64 = undefined; + var x105: u1 = undefined; + subborrowxU64(&x104, &x105, x103, x96, 0xffffffff); + var x106: u64 = undefined; + var x107: u1 = undefined; + subborrowxU64(&x106, &x107, x105, x98, cast(u64, 0x0)); + var x108: u64 = undefined; + var x109: u1 = undefined; + subborrowxU64(&x108, &x109, x107, x100, 0xffffffff00000001); + var x110: u64 = undefined; + var x111: u1 = undefined; + subborrowxU64(&x110, &x111, x109, cast(u64, x101), cast(u64, 0x0)); + var x112: u64 = undefined; + var x113: u1 = undefined; + addcarryxU64(&x112, &x113, 0x0, x6, cast(u64, 0x1)); + const x114 = ((x80 >> 1) | ((x82 << 63) & 0xffffffffffffffff)); + const x115 = ((x82 >> 1) | ((x84 << 63) & 0xffffffffffffffff)); + const x116 = ((x84 >> 1) | ((x86 << 63) & 0xffffffffffffffff)); + const x117 = ((x86 >> 1) | ((x88 << 63) & 0xffffffffffffffff)); + const x118 = ((x88 & 0x8000000000000000) | (x88 >> 1)); + var x119: u64 = undefined; + cmovznzU64(&x119, x48, x39, x31); + var x120: u64 = undefined; + cmovznzU64(&x120, x48, x41, x33); + var x121: u64 = undefined; + cmovznzU64(&x121, x48, x43, x35); + var x122: u64 = undefined; + cmovznzU64(&x122, x48, x45, x37); + var x123: u64 = undefined; + cmovznzU64(&x123, x111, x102, x94); + var x124: u64 = undefined; + cmovznzU64(&x124, x111, x104, x96); + var x125: u64 = undefined; + cmovznzU64(&x125, x111, x106, x98); + var x126: u64 = undefined; + cmovznzU64(&x126, x111, x108, x100); + out1.* = x112; + out2[0] = x7; + out2[1] = x8; + out2[2] = x9; + out2[3] = x10; + out2[4] = x11; + out3[0] = x114; + out3[1] = x115; + out3[2] = x116; + out3[3] = x117; + out3[4] = x118; + out4[0] = x119; + out4[1] = x120; + out4[2] = x121; + out4[3] = x122; + out5[0] = x123; + out5[1] = x124; + out5[2] = x125; + out5[3] = x126; +} + +/// The function divstepPrecomp returns the precomputed value for Bernstein-Yang-inversion (in montgomery form). +/// Postconditions: +/// eval (from_montgomery out1) = ⌊(m - 1) / 2⌋^(if (log2 m) + 1 < 46 then ⌊(49 * ((log2 m) + 1) + 80) / 17⌋ else ⌊(49 * ((log2 m) + 1) + 57) / 17⌋) +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn divstepPrecomp(out1: *[4]u64) void { + @setRuntimeSafety(mode == .Debug); + + out1[0] = 0x67ffffffb8000000; + out1[1] = 0xc000000038000000; + out1[2] = 0xd80000007fffffff; + out1[3] = 0x2fffffffffffffff; +} diff --git a/zig/lib/std/crypto/pcurves/p256/p256_scalar_64.zig b/zig/lib/std/crypto/pcurves/p256/p256_scalar_64.zig new file mode 100644 index 0000000000..7e3cc211f5 --- /dev/null +++ b/zig/lib/std/crypto/pcurves/p256/p256_scalar_64.zig @@ -0,0 +1,2016 @@ +// Autogenerated: './src/ExtractionOCaml/word_by_word_montgomery' --lang Zig --internal-static --public-function-case camelCase --private-function-case camelCase --no-prefix-fiat --package-name p256-scalar '' 64 115792089210356248762697446949407573529996955224135760342422259061068512044369 +// curve description (via package name): p256-scalar +// machine_wordsize = 64 (from "64") +// requested operations: (all) +// m = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551 (from "115792089210356248762697446949407573529996955224135760342422259061068512044369") +// +// NOTE: In addition to the bounds specified above each function, all +// functions synthesized for this Montgomery arithmetic require the +// input to be strictly less than the prime modulus (m), and also +// require the input to be in the unique saturated representation. +// All functions also ensure that these two properties are true of +// return values. +// +// Computed values: +// eval z = z[0] + (z[1] << 64) + (z[2] << 128) + (z[3] << 192) +// bytes_eval z = z[0] + (z[1] << 8) + (z[2] << 16) + (z[3] << 24) + (z[4] << 32) + (z[5] << 40) + (z[6] << 48) + (z[7] << 56) + (z[8] << 64) + (z[9] << 72) + (z[10] << 80) + (z[11] << 88) + (z[12] << 96) + (z[13] << 104) + (z[14] << 112) + (z[15] << 120) + (z[16] << 128) + (z[17] << 136) + (z[18] << 144) + (z[19] << 152) + (z[20] << 160) + (z[21] << 168) + (z[22] << 176) + (z[23] << 184) + (z[24] << 192) + (z[25] << 200) + (z[26] << 208) + (z[27] << 216) + (z[28] << 224) + (z[29] << 232) + (z[30] << 240) + (z[31] << 248) +// twos_complement_eval z = let x1 := z[0] + (z[1] << 64) + (z[2] << 128) + (z[3] << 192) in +// if x1 & (2^256-1) < 2^255 then x1 & (2^256-1) else (x1 & (2^256-1)) - 2^256 + +const std = @import("std"); +const cast = std.meta.cast; +const mode = std.builtin.mode; // Checked arithmetic is disabled in non-debug modes to avoid side channels + +pub const Limbs = [4]u64; + +/// The function addcarryxU64 is an addition with carry. +/// Postconditions: +/// out1 = (arg1 + arg2 + arg3) mod 2^64 +/// out2 = ⌊(arg1 + arg2 + arg3) / 2^64⌋ +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// arg3: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [0x0 ~> 0x1] +fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) callconv(.Inline) void { + @setRuntimeSafety(mode == .Debug); + + var t: u64 = undefined; + const carry1 = @addWithOverflow(u64, arg2, arg3, &t); + const carry2 = @addWithOverflow(u64, t, arg1, out1); + out2.* = @boolToInt(carry1) | @boolToInt(carry2); +} + +/// The function subborrowxU64 is a subtraction with borrow. +/// Postconditions: +/// out1 = (-arg1 + arg2 + -arg3) mod 2^64 +/// out2 = -⌊(-arg1 + arg2 + -arg3) / 2^64⌋ +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// arg3: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [0x0 ~> 0x1] +fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) callconv(.Inline) void { + @setRuntimeSafety(mode == .Debug); + + var t: u64 = undefined; + const carry1 = @subWithOverflow(u64, arg2, arg3, &t); + const carry2 = @subWithOverflow(u64, t, arg1, out1); + out2.* = @boolToInt(carry1) | @boolToInt(carry2); +} + +/// The function mulxU64 is a multiplication, returning the full double-width result. +/// Postconditions: +/// out1 = (arg1 * arg2) mod 2^64 +/// out2 = ⌊arg1 * arg2 / 2^64⌋ +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0xffffffffffffffff] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [0x0 ~> 0xffffffffffffffff] +fn mulxU64(out1: *u64, out2: *u64, arg1: u64, arg2: u64) callconv(.Inline) void { + @setRuntimeSafety(mode == .Debug); + + const x = @as(u128, arg1) * @as(u128, arg2); + out1.* = @truncate(u64, x); + out2.* = @truncate(u64, x >> 64); +} + +/// The function cmovznzU64 is a single-word conditional move. +/// Postconditions: +/// out1 = (if arg1 = 0 then arg2 else arg3) +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// arg3: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +fn cmovznzU64(out1: *u64, arg1: u1, arg2: u64, arg3: u64) callconv(.Inline) void { + @setRuntimeSafety(mode == .Debug); + + const mask = 0 -% @as(u64, arg1); + out1.* = (mask & arg3) | ((~mask) & arg2); +} + +/// The function mul multiplies two field elements in the Montgomery domain. +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// 0 ≤ eval arg2 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg2)) mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn mul(out1: *[4]u64, arg1: [4]u64, arg2: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[1]); + const x2 = (arg1[2]); + const x3 = (arg1[3]); + const x4 = (arg1[0]); + var x5: u64 = undefined; + var x6: u64 = undefined; + mulxU64(&x5, &x6, x4, (arg2[3])); + var x7: u64 = undefined; + var x8: u64 = undefined; + mulxU64(&x7, &x8, x4, (arg2[2])); + var x9: u64 = undefined; + var x10: u64 = undefined; + mulxU64(&x9, &x10, x4, (arg2[1])); + var x11: u64 = undefined; + var x12: u64 = undefined; + mulxU64(&x11, &x12, x4, (arg2[0])); + var x13: u64 = undefined; + var x14: u1 = undefined; + addcarryxU64(&x13, &x14, 0x0, x12, x9); + var x15: u64 = undefined; + var x16: u1 = undefined; + addcarryxU64(&x15, &x16, x14, x10, x7); + var x17: u64 = undefined; + var x18: u1 = undefined; + addcarryxU64(&x17, &x18, x16, x8, x5); + const x19 = (cast(u64, x18) + x6); + var x20: u64 = undefined; + var x21: u64 = undefined; + mulxU64(&x20, &x21, x11, 0xccd1c8aaee00bc4f); + var x22: u64 = undefined; + var x23: u64 = undefined; + mulxU64(&x22, &x23, x20, 0xffffffff00000000); + var x24: u64 = undefined; + var x25: u64 = undefined; + mulxU64(&x24, &x25, x20, 0xffffffffffffffff); + var x26: u64 = undefined; + var x27: u64 = undefined; + mulxU64(&x26, &x27, x20, 0xbce6faada7179e84); + var x28: u64 = undefined; + var x29: u64 = undefined; + mulxU64(&x28, &x29, x20, 0xf3b9cac2fc632551); + var x30: u64 = undefined; + var x31: u1 = undefined; + addcarryxU64(&x30, &x31, 0x0, x29, x26); + var x32: u64 = undefined; + var x33: u1 = undefined; + addcarryxU64(&x32, &x33, x31, x27, x24); + var x34: u64 = undefined; + var x35: u1 = undefined; + addcarryxU64(&x34, &x35, x33, x25, x22); + const x36 = (cast(u64, x35) + x23); + var x37: u64 = undefined; + var x38: u1 = undefined; + addcarryxU64(&x37, &x38, 0x0, x11, x28); + var x39: u64 = undefined; + var x40: u1 = undefined; + addcarryxU64(&x39, &x40, x38, x13, x30); + var x41: u64 = undefined; + var x42: u1 = undefined; + addcarryxU64(&x41, &x42, x40, x15, x32); + var x43: u64 = undefined; + var x44: u1 = undefined; + addcarryxU64(&x43, &x44, x42, x17, x34); + var x45: u64 = undefined; + var x46: u1 = undefined; + addcarryxU64(&x45, &x46, x44, x19, x36); + var x47: u64 = undefined; + var x48: u64 = undefined; + mulxU64(&x47, &x48, x1, (arg2[3])); + var x49: u64 = undefined; + var x50: u64 = undefined; + mulxU64(&x49, &x50, x1, (arg2[2])); + var x51: u64 = undefined; + var x52: u64 = undefined; + mulxU64(&x51, &x52, x1, (arg2[1])); + var x53: u64 = undefined; + var x54: u64 = undefined; + mulxU64(&x53, &x54, x1, (arg2[0])); + var x55: u64 = undefined; + var x56: u1 = undefined; + addcarryxU64(&x55, &x56, 0x0, x54, x51); + var x57: u64 = undefined; + var x58: u1 = undefined; + addcarryxU64(&x57, &x58, x56, x52, x49); + var x59: u64 = undefined; + var x60: u1 = undefined; + addcarryxU64(&x59, &x60, x58, x50, x47); + const x61 = (cast(u64, x60) + x48); + var x62: u64 = undefined; + var x63: u1 = undefined; + addcarryxU64(&x62, &x63, 0x0, x39, x53); + var x64: u64 = undefined; + var x65: u1 = undefined; + addcarryxU64(&x64, &x65, x63, x41, x55); + var x66: u64 = undefined; + var x67: u1 = undefined; + addcarryxU64(&x66, &x67, x65, x43, x57); + var x68: u64 = undefined; + var x69: u1 = undefined; + addcarryxU64(&x68, &x69, x67, x45, x59); + var x70: u64 = undefined; + var x71: u1 = undefined; + addcarryxU64(&x70, &x71, x69, cast(u64, x46), x61); + var x72: u64 = undefined; + var x73: u64 = undefined; + mulxU64(&x72, &x73, x62, 0xccd1c8aaee00bc4f); + var x74: u64 = undefined; + var x75: u64 = undefined; + mulxU64(&x74, &x75, x72, 0xffffffff00000000); + var x76: u64 = undefined; + var x77: u64 = undefined; + mulxU64(&x76, &x77, x72, 0xffffffffffffffff); + var x78: u64 = undefined; + var x79: u64 = undefined; + mulxU64(&x78, &x79, x72, 0xbce6faada7179e84); + var x80: u64 = undefined; + var x81: u64 = undefined; + mulxU64(&x80, &x81, x72, 0xf3b9cac2fc632551); + var x82: u64 = undefined; + var x83: u1 = undefined; + addcarryxU64(&x82, &x83, 0x0, x81, x78); + var x84: u64 = undefined; + var x85: u1 = undefined; + addcarryxU64(&x84, &x85, x83, x79, x76); + var x86: u64 = undefined; + var x87: u1 = undefined; + addcarryxU64(&x86, &x87, x85, x77, x74); + const x88 = (cast(u64, x87) + x75); + var x89: u64 = undefined; + var x90: u1 = undefined; + addcarryxU64(&x89, &x90, 0x0, x62, x80); + var x91: u64 = undefined; + var x92: u1 = undefined; + addcarryxU64(&x91, &x92, x90, x64, x82); + var x93: u64 = undefined; + var x94: u1 = undefined; + addcarryxU64(&x93, &x94, x92, x66, x84); + var x95: u64 = undefined; + var x96: u1 = undefined; + addcarryxU64(&x95, &x96, x94, x68, x86); + var x97: u64 = undefined; + var x98: u1 = undefined; + addcarryxU64(&x97, &x98, x96, x70, x88); + const x99 = (cast(u64, x98) + cast(u64, x71)); + var x100: u64 = undefined; + var x101: u64 = undefined; + mulxU64(&x100, &x101, x2, (arg2[3])); + var x102: u64 = undefined; + var x103: u64 = undefined; + mulxU64(&x102, &x103, x2, (arg2[2])); + var x104: u64 = undefined; + var x105: u64 = undefined; + mulxU64(&x104, &x105, x2, (arg2[1])); + var x106: u64 = undefined; + var x107: u64 = undefined; + mulxU64(&x106, &x107, x2, (arg2[0])); + var x108: u64 = undefined; + var x109: u1 = undefined; + addcarryxU64(&x108, &x109, 0x0, x107, x104); + var x110: u64 = undefined; + var x111: u1 = undefined; + addcarryxU64(&x110, &x111, x109, x105, x102); + var x112: u64 = undefined; + var x113: u1 = undefined; + addcarryxU64(&x112, &x113, x111, x103, x100); + const x114 = (cast(u64, x113) + x101); + var x115: u64 = undefined; + var x116: u1 = undefined; + addcarryxU64(&x115, &x116, 0x0, x91, x106); + var x117: u64 = undefined; + var x118: u1 = undefined; + addcarryxU64(&x117, &x118, x116, x93, x108); + var x119: u64 = undefined; + var x120: u1 = undefined; + addcarryxU64(&x119, &x120, x118, x95, x110); + var x121: u64 = undefined; + var x122: u1 = undefined; + addcarryxU64(&x121, &x122, x120, x97, x112); + var x123: u64 = undefined; + var x124: u1 = undefined; + addcarryxU64(&x123, &x124, x122, x99, x114); + var x125: u64 = undefined; + var x126: u64 = undefined; + mulxU64(&x125, &x126, x115, 0xccd1c8aaee00bc4f); + var x127: u64 = undefined; + var x128: u64 = undefined; + mulxU64(&x127, &x128, x125, 0xffffffff00000000); + var x129: u64 = undefined; + var x130: u64 = undefined; + mulxU64(&x129, &x130, x125, 0xffffffffffffffff); + var x131: u64 = undefined; + var x132: u64 = undefined; + mulxU64(&x131, &x132, x125, 0xbce6faada7179e84); + var x133: u64 = undefined; + var x134: u64 = undefined; + mulxU64(&x133, &x134, x125, 0xf3b9cac2fc632551); + var x135: u64 = undefined; + var x136: u1 = undefined; + addcarryxU64(&x135, &x136, 0x0, x134, x131); + var x137: u64 = undefined; + var x138: u1 = undefined; + addcarryxU64(&x137, &x138, x136, x132, x129); + var x139: u64 = undefined; + var x140: u1 = undefined; + addcarryxU64(&x139, &x140, x138, x130, x127); + const x141 = (cast(u64, x140) + x128); + var x142: u64 = undefined; + var x143: u1 = undefined; + addcarryxU64(&x142, &x143, 0x0, x115, x133); + var x144: u64 = undefined; + var x145: u1 = undefined; + addcarryxU64(&x144, &x145, x143, x117, x135); + var x146: u64 = undefined; + var x147: u1 = undefined; + addcarryxU64(&x146, &x147, x145, x119, x137); + var x148: u64 = undefined; + var x149: u1 = undefined; + addcarryxU64(&x148, &x149, x147, x121, x139); + var x150: u64 = undefined; + var x151: u1 = undefined; + addcarryxU64(&x150, &x151, x149, x123, x141); + const x152 = (cast(u64, x151) + cast(u64, x124)); + var x153: u64 = undefined; + var x154: u64 = undefined; + mulxU64(&x153, &x154, x3, (arg2[3])); + var x155: u64 = undefined; + var x156: u64 = undefined; + mulxU64(&x155, &x156, x3, (arg2[2])); + var x157: u64 = undefined; + var x158: u64 = undefined; + mulxU64(&x157, &x158, x3, (arg2[1])); + var x159: u64 = undefined; + var x160: u64 = undefined; + mulxU64(&x159, &x160, x3, (arg2[0])); + var x161: u64 = undefined; + var x162: u1 = undefined; + addcarryxU64(&x161, &x162, 0x0, x160, x157); + var x163: u64 = undefined; + var x164: u1 = undefined; + addcarryxU64(&x163, &x164, x162, x158, x155); + var x165: u64 = undefined; + var x166: u1 = undefined; + addcarryxU64(&x165, &x166, x164, x156, x153); + const x167 = (cast(u64, x166) + x154); + var x168: u64 = undefined; + var x169: u1 = undefined; + addcarryxU64(&x168, &x169, 0x0, x144, x159); + var x170: u64 = undefined; + var x171: u1 = undefined; + addcarryxU64(&x170, &x171, x169, x146, x161); + var x172: u64 = undefined; + var x173: u1 = undefined; + addcarryxU64(&x172, &x173, x171, x148, x163); + var x174: u64 = undefined; + var x175: u1 = undefined; + addcarryxU64(&x174, &x175, x173, x150, x165); + var x176: u64 = undefined; + var x177: u1 = undefined; + addcarryxU64(&x176, &x177, x175, x152, x167); + var x178: u64 = undefined; + var x179: u64 = undefined; + mulxU64(&x178, &x179, x168, 0xccd1c8aaee00bc4f); + var x180: u64 = undefined; + var x181: u64 = undefined; + mulxU64(&x180, &x181, x178, 0xffffffff00000000); + var x182: u64 = undefined; + var x183: u64 = undefined; + mulxU64(&x182, &x183, x178, 0xffffffffffffffff); + var x184: u64 = undefined; + var x185: u64 = undefined; + mulxU64(&x184, &x185, x178, 0xbce6faada7179e84); + var x186: u64 = undefined; + var x187: u64 = undefined; + mulxU64(&x186, &x187, x178, 0xf3b9cac2fc632551); + var x188: u64 = undefined; + var x189: u1 = undefined; + addcarryxU64(&x188, &x189, 0x0, x187, x184); + var x190: u64 = undefined; + var x191: u1 = undefined; + addcarryxU64(&x190, &x191, x189, x185, x182); + var x192: u64 = undefined; + var x193: u1 = undefined; + addcarryxU64(&x192, &x193, x191, x183, x180); + const x194 = (cast(u64, x193) + x181); + var x195: u64 = undefined; + var x196: u1 = undefined; + addcarryxU64(&x195, &x196, 0x0, x168, x186); + var x197: u64 = undefined; + var x198: u1 = undefined; + addcarryxU64(&x197, &x198, x196, x170, x188); + var x199: u64 = undefined; + var x200: u1 = undefined; + addcarryxU64(&x199, &x200, x198, x172, x190); + var x201: u64 = undefined; + var x202: u1 = undefined; + addcarryxU64(&x201, &x202, x200, x174, x192); + var x203: u64 = undefined; + var x204: u1 = undefined; + addcarryxU64(&x203, &x204, x202, x176, x194); + const x205 = (cast(u64, x204) + cast(u64, x177)); + var x206: u64 = undefined; + var x207: u1 = undefined; + subborrowxU64(&x206, &x207, 0x0, x197, 0xf3b9cac2fc632551); + var x208: u64 = undefined; + var x209: u1 = undefined; + subborrowxU64(&x208, &x209, x207, x199, 0xbce6faada7179e84); + var x210: u64 = undefined; + var x211: u1 = undefined; + subborrowxU64(&x210, &x211, x209, x201, 0xffffffffffffffff); + var x212: u64 = undefined; + var x213: u1 = undefined; + subborrowxU64(&x212, &x213, x211, x203, 0xffffffff00000000); + var x214: u64 = undefined; + var x215: u1 = undefined; + subborrowxU64(&x214, &x215, x213, x205, cast(u64, 0x0)); + var x216: u64 = undefined; + cmovznzU64(&x216, x215, x206, x197); + var x217: u64 = undefined; + cmovznzU64(&x217, x215, x208, x199); + var x218: u64 = undefined; + cmovznzU64(&x218, x215, x210, x201); + var x219: u64 = undefined; + cmovznzU64(&x219, x215, x212, x203); + out1[0] = x216; + out1[1] = x217; + out1[2] = x218; + out1[3] = x219; +} + +/// The function square squares a field element in the Montgomery domain. +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg1)) mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn square(out1: *[4]u64, arg1: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[1]); + const x2 = (arg1[2]); + const x3 = (arg1[3]); + const x4 = (arg1[0]); + var x5: u64 = undefined; + var x6: u64 = undefined; + mulxU64(&x5, &x6, x4, (arg1[3])); + var x7: u64 = undefined; + var x8: u64 = undefined; + mulxU64(&x7, &x8, x4, (arg1[2])); + var x9: u64 = undefined; + var x10: u64 = undefined; + mulxU64(&x9, &x10, x4, (arg1[1])); + var x11: u64 = undefined; + var x12: u64 = undefined; + mulxU64(&x11, &x12, x4, (arg1[0])); + var x13: u64 = undefined; + var x14: u1 = undefined; + addcarryxU64(&x13, &x14, 0x0, x12, x9); + var x15: u64 = undefined; + var x16: u1 = undefined; + addcarryxU64(&x15, &x16, x14, x10, x7); + var x17: u64 = undefined; + var x18: u1 = undefined; + addcarryxU64(&x17, &x18, x16, x8, x5); + const x19 = (cast(u64, x18) + x6); + var x20: u64 = undefined; + var x21: u64 = undefined; + mulxU64(&x20, &x21, x11, 0xccd1c8aaee00bc4f); + var x22: u64 = undefined; + var x23: u64 = undefined; + mulxU64(&x22, &x23, x20, 0xffffffff00000000); + var x24: u64 = undefined; + var x25: u64 = undefined; + mulxU64(&x24, &x25, x20, 0xffffffffffffffff); + var x26: u64 = undefined; + var x27: u64 = undefined; + mulxU64(&x26, &x27, x20, 0xbce6faada7179e84); + var x28: u64 = undefined; + var x29: u64 = undefined; + mulxU64(&x28, &x29, x20, 0xf3b9cac2fc632551); + var x30: u64 = undefined; + var x31: u1 = undefined; + addcarryxU64(&x30, &x31, 0x0, x29, x26); + var x32: u64 = undefined; + var x33: u1 = undefined; + addcarryxU64(&x32, &x33, x31, x27, x24); + var x34: u64 = undefined; + var x35: u1 = undefined; + addcarryxU64(&x34, &x35, x33, x25, x22); + const x36 = (cast(u64, x35) + x23); + var x37: u64 = undefined; + var x38: u1 = undefined; + addcarryxU64(&x37, &x38, 0x0, x11, x28); + var x39: u64 = undefined; + var x40: u1 = undefined; + addcarryxU64(&x39, &x40, x38, x13, x30); + var x41: u64 = undefined; + var x42: u1 = undefined; + addcarryxU64(&x41, &x42, x40, x15, x32); + var x43: u64 = undefined; + var x44: u1 = undefined; + addcarryxU64(&x43, &x44, x42, x17, x34); + var x45: u64 = undefined; + var x46: u1 = undefined; + addcarryxU64(&x45, &x46, x44, x19, x36); + var x47: u64 = undefined; + var x48: u64 = undefined; + mulxU64(&x47, &x48, x1, (arg1[3])); + var x49: u64 = undefined; + var x50: u64 = undefined; + mulxU64(&x49, &x50, x1, (arg1[2])); + var x51: u64 = undefined; + var x52: u64 = undefined; + mulxU64(&x51, &x52, x1, (arg1[1])); + var x53: u64 = undefined; + var x54: u64 = undefined; + mulxU64(&x53, &x54, x1, (arg1[0])); + var x55: u64 = undefined; + var x56: u1 = undefined; + addcarryxU64(&x55, &x56, 0x0, x54, x51); + var x57: u64 = undefined; + var x58: u1 = undefined; + addcarryxU64(&x57, &x58, x56, x52, x49); + var x59: u64 = undefined; + var x60: u1 = undefined; + addcarryxU64(&x59, &x60, x58, x50, x47); + const x61 = (cast(u64, x60) + x48); + var x62: u64 = undefined; + var x63: u1 = undefined; + addcarryxU64(&x62, &x63, 0x0, x39, x53); + var x64: u64 = undefined; + var x65: u1 = undefined; + addcarryxU64(&x64, &x65, x63, x41, x55); + var x66: u64 = undefined; + var x67: u1 = undefined; + addcarryxU64(&x66, &x67, x65, x43, x57); + var x68: u64 = undefined; + var x69: u1 = undefined; + addcarryxU64(&x68, &x69, x67, x45, x59); + var x70: u64 = undefined; + var x71: u1 = undefined; + addcarryxU64(&x70, &x71, x69, cast(u64, x46), x61); + var x72: u64 = undefined; + var x73: u64 = undefined; + mulxU64(&x72, &x73, x62, 0xccd1c8aaee00bc4f); + var x74: u64 = undefined; + var x75: u64 = undefined; + mulxU64(&x74, &x75, x72, 0xffffffff00000000); + var x76: u64 = undefined; + var x77: u64 = undefined; + mulxU64(&x76, &x77, x72, 0xffffffffffffffff); + var x78: u64 = undefined; + var x79: u64 = undefined; + mulxU64(&x78, &x79, x72, 0xbce6faada7179e84); + var x80: u64 = undefined; + var x81: u64 = undefined; + mulxU64(&x80, &x81, x72, 0xf3b9cac2fc632551); + var x82: u64 = undefined; + var x83: u1 = undefined; + addcarryxU64(&x82, &x83, 0x0, x81, x78); + var x84: u64 = undefined; + var x85: u1 = undefined; + addcarryxU64(&x84, &x85, x83, x79, x76); + var x86: u64 = undefined; + var x87: u1 = undefined; + addcarryxU64(&x86, &x87, x85, x77, x74); + const x88 = (cast(u64, x87) + x75); + var x89: u64 = undefined; + var x90: u1 = undefined; + addcarryxU64(&x89, &x90, 0x0, x62, x80); + var x91: u64 = undefined; + var x92: u1 = undefined; + addcarryxU64(&x91, &x92, x90, x64, x82); + var x93: u64 = undefined; + var x94: u1 = undefined; + addcarryxU64(&x93, &x94, x92, x66, x84); + var x95: u64 = undefined; + var x96: u1 = undefined; + addcarryxU64(&x95, &x96, x94, x68, x86); + var x97: u64 = undefined; + var x98: u1 = undefined; + addcarryxU64(&x97, &x98, x96, x70, x88); + const x99 = (cast(u64, x98) + cast(u64, x71)); + var x100: u64 = undefined; + var x101: u64 = undefined; + mulxU64(&x100, &x101, x2, (arg1[3])); + var x102: u64 = undefined; + var x103: u64 = undefined; + mulxU64(&x102, &x103, x2, (arg1[2])); + var x104: u64 = undefined; + var x105: u64 = undefined; + mulxU64(&x104, &x105, x2, (arg1[1])); + var x106: u64 = undefined; + var x107: u64 = undefined; + mulxU64(&x106, &x107, x2, (arg1[0])); + var x108: u64 = undefined; + var x109: u1 = undefined; + addcarryxU64(&x108, &x109, 0x0, x107, x104); + var x110: u64 = undefined; + var x111: u1 = undefined; + addcarryxU64(&x110, &x111, x109, x105, x102); + var x112: u64 = undefined; + var x113: u1 = undefined; + addcarryxU64(&x112, &x113, x111, x103, x100); + const x114 = (cast(u64, x113) + x101); + var x115: u64 = undefined; + var x116: u1 = undefined; + addcarryxU64(&x115, &x116, 0x0, x91, x106); + var x117: u64 = undefined; + var x118: u1 = undefined; + addcarryxU64(&x117, &x118, x116, x93, x108); + var x119: u64 = undefined; + var x120: u1 = undefined; + addcarryxU64(&x119, &x120, x118, x95, x110); + var x121: u64 = undefined; + var x122: u1 = undefined; + addcarryxU64(&x121, &x122, x120, x97, x112); + var x123: u64 = undefined; + var x124: u1 = undefined; + addcarryxU64(&x123, &x124, x122, x99, x114); + var x125: u64 = undefined; + var x126: u64 = undefined; + mulxU64(&x125, &x126, x115, 0xccd1c8aaee00bc4f); + var x127: u64 = undefined; + var x128: u64 = undefined; + mulxU64(&x127, &x128, x125, 0xffffffff00000000); + var x129: u64 = undefined; + var x130: u64 = undefined; + mulxU64(&x129, &x130, x125, 0xffffffffffffffff); + var x131: u64 = undefined; + var x132: u64 = undefined; + mulxU64(&x131, &x132, x125, 0xbce6faada7179e84); + var x133: u64 = undefined; + var x134: u64 = undefined; + mulxU64(&x133, &x134, x125, 0xf3b9cac2fc632551); + var x135: u64 = undefined; + var x136: u1 = undefined; + addcarryxU64(&x135, &x136, 0x0, x134, x131); + var x137: u64 = undefined; + var x138: u1 = undefined; + addcarryxU64(&x137, &x138, x136, x132, x129); + var x139: u64 = undefined; + var x140: u1 = undefined; + addcarryxU64(&x139, &x140, x138, x130, x127); + const x141 = (cast(u64, x140) + x128); + var x142: u64 = undefined; + var x143: u1 = undefined; + addcarryxU64(&x142, &x143, 0x0, x115, x133); + var x144: u64 = undefined; + var x145: u1 = undefined; + addcarryxU64(&x144, &x145, x143, x117, x135); + var x146: u64 = undefined; + var x147: u1 = undefined; + addcarryxU64(&x146, &x147, x145, x119, x137); + var x148: u64 = undefined; + var x149: u1 = undefined; + addcarryxU64(&x148, &x149, x147, x121, x139); + var x150: u64 = undefined; + var x151: u1 = undefined; + addcarryxU64(&x150, &x151, x149, x123, x141); + const x152 = (cast(u64, x151) + cast(u64, x124)); + var x153: u64 = undefined; + var x154: u64 = undefined; + mulxU64(&x153, &x154, x3, (arg1[3])); + var x155: u64 = undefined; + var x156: u64 = undefined; + mulxU64(&x155, &x156, x3, (arg1[2])); + var x157: u64 = undefined; + var x158: u64 = undefined; + mulxU64(&x157, &x158, x3, (arg1[1])); + var x159: u64 = undefined; + var x160: u64 = undefined; + mulxU64(&x159, &x160, x3, (arg1[0])); + var x161: u64 = undefined; + var x162: u1 = undefined; + addcarryxU64(&x161, &x162, 0x0, x160, x157); + var x163: u64 = undefined; + var x164: u1 = undefined; + addcarryxU64(&x163, &x164, x162, x158, x155); + var x165: u64 = undefined; + var x166: u1 = undefined; + addcarryxU64(&x165, &x166, x164, x156, x153); + const x167 = (cast(u64, x166) + x154); + var x168: u64 = undefined; + var x169: u1 = undefined; + addcarryxU64(&x168, &x169, 0x0, x144, x159); + var x170: u64 = undefined; + var x171: u1 = undefined; + addcarryxU64(&x170, &x171, x169, x146, x161); + var x172: u64 = undefined; + var x173: u1 = undefined; + addcarryxU64(&x172, &x173, x171, x148, x163); + var x174: u64 = undefined; + var x175: u1 = undefined; + addcarryxU64(&x174, &x175, x173, x150, x165); + var x176: u64 = undefined; + var x177: u1 = undefined; + addcarryxU64(&x176, &x177, x175, x152, x167); + var x178: u64 = undefined; + var x179: u64 = undefined; + mulxU64(&x178, &x179, x168, 0xccd1c8aaee00bc4f); + var x180: u64 = undefined; + var x181: u64 = undefined; + mulxU64(&x180, &x181, x178, 0xffffffff00000000); + var x182: u64 = undefined; + var x183: u64 = undefined; + mulxU64(&x182, &x183, x178, 0xffffffffffffffff); + var x184: u64 = undefined; + var x185: u64 = undefined; + mulxU64(&x184, &x185, x178, 0xbce6faada7179e84); + var x186: u64 = undefined; + var x187: u64 = undefined; + mulxU64(&x186, &x187, x178, 0xf3b9cac2fc632551); + var x188: u64 = undefined; + var x189: u1 = undefined; + addcarryxU64(&x188, &x189, 0x0, x187, x184); + var x190: u64 = undefined; + var x191: u1 = undefined; + addcarryxU64(&x190, &x191, x189, x185, x182); + var x192: u64 = undefined; + var x193: u1 = undefined; + addcarryxU64(&x192, &x193, x191, x183, x180); + const x194 = (cast(u64, x193) + x181); + var x195: u64 = undefined; + var x196: u1 = undefined; + addcarryxU64(&x195, &x196, 0x0, x168, x186); + var x197: u64 = undefined; + var x198: u1 = undefined; + addcarryxU64(&x197, &x198, x196, x170, x188); + var x199: u64 = undefined; + var x200: u1 = undefined; + addcarryxU64(&x199, &x200, x198, x172, x190); + var x201: u64 = undefined; + var x202: u1 = undefined; + addcarryxU64(&x201, &x202, x200, x174, x192); + var x203: u64 = undefined; + var x204: u1 = undefined; + addcarryxU64(&x203, &x204, x202, x176, x194); + const x205 = (cast(u64, x204) + cast(u64, x177)); + var x206: u64 = undefined; + var x207: u1 = undefined; + subborrowxU64(&x206, &x207, 0x0, x197, 0xf3b9cac2fc632551); + var x208: u64 = undefined; + var x209: u1 = undefined; + subborrowxU64(&x208, &x209, x207, x199, 0xbce6faada7179e84); + var x210: u64 = undefined; + var x211: u1 = undefined; + subborrowxU64(&x210, &x211, x209, x201, 0xffffffffffffffff); + var x212: u64 = undefined; + var x213: u1 = undefined; + subborrowxU64(&x212, &x213, x211, x203, 0xffffffff00000000); + var x214: u64 = undefined; + var x215: u1 = undefined; + subborrowxU64(&x214, &x215, x213, x205, cast(u64, 0x0)); + var x216: u64 = undefined; + cmovznzU64(&x216, x215, x206, x197); + var x217: u64 = undefined; + cmovznzU64(&x217, x215, x208, x199); + var x218: u64 = undefined; + cmovznzU64(&x218, x215, x210, x201); + var x219: u64 = undefined; + cmovznzU64(&x219, x215, x212, x203); + out1[0] = x216; + out1[1] = x217; + out1[2] = x218; + out1[3] = x219; +} + +/// The function add adds two field elements in the Montgomery domain. +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// 0 ≤ eval arg2 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) + eval (from_montgomery arg2)) mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn add(out1: *[4]u64, arg1: [4]u64, arg2: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + addcarryxU64(&x1, &x2, 0x0, (arg1[0]), (arg2[0])); + var x3: u64 = undefined; + var x4: u1 = undefined; + addcarryxU64(&x3, &x4, x2, (arg1[1]), (arg2[1])); + var x5: u64 = undefined; + var x6: u1 = undefined; + addcarryxU64(&x5, &x6, x4, (arg1[2]), (arg2[2])); + var x7: u64 = undefined; + var x8: u1 = undefined; + addcarryxU64(&x7, &x8, x6, (arg1[3]), (arg2[3])); + var x9: u64 = undefined; + var x10: u1 = undefined; + subborrowxU64(&x9, &x10, 0x0, x1, 0xf3b9cac2fc632551); + var x11: u64 = undefined; + var x12: u1 = undefined; + subborrowxU64(&x11, &x12, x10, x3, 0xbce6faada7179e84); + var x13: u64 = undefined; + var x14: u1 = undefined; + subborrowxU64(&x13, &x14, x12, x5, 0xffffffffffffffff); + var x15: u64 = undefined; + var x16: u1 = undefined; + subborrowxU64(&x15, &x16, x14, x7, 0xffffffff00000000); + var x17: u64 = undefined; + var x18: u1 = undefined; + subborrowxU64(&x17, &x18, x16, cast(u64, x8), cast(u64, 0x0)); + var x19: u64 = undefined; + cmovznzU64(&x19, x18, x9, x1); + var x20: u64 = undefined; + cmovznzU64(&x20, x18, x11, x3); + var x21: u64 = undefined; + cmovznzU64(&x21, x18, x13, x5); + var x22: u64 = undefined; + cmovznzU64(&x22, x18, x15, x7); + out1[0] = x19; + out1[1] = x20; + out1[2] = x21; + out1[3] = x22; +} + +/// The function sub subtracts two field elements in the Montgomery domain. +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// 0 ≤ eval arg2 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) - eval (from_montgomery arg2)) mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn sub(out1: *[4]u64, arg1: [4]u64, arg2: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + subborrowxU64(&x1, &x2, 0x0, (arg1[0]), (arg2[0])); + var x3: u64 = undefined; + var x4: u1 = undefined; + subborrowxU64(&x3, &x4, x2, (arg1[1]), (arg2[1])); + var x5: u64 = undefined; + var x6: u1 = undefined; + subborrowxU64(&x5, &x6, x4, (arg1[2]), (arg2[2])); + var x7: u64 = undefined; + var x8: u1 = undefined; + subborrowxU64(&x7, &x8, x6, (arg1[3]), (arg2[3])); + var x9: u64 = undefined; + cmovznzU64(&x9, x8, cast(u64, 0x0), 0xffffffffffffffff); + var x10: u64 = undefined; + var x11: u1 = undefined; + addcarryxU64(&x10, &x11, 0x0, x1, (x9 & 0xf3b9cac2fc632551)); + var x12: u64 = undefined; + var x13: u1 = undefined; + addcarryxU64(&x12, &x13, x11, x3, (x9 & 0xbce6faada7179e84)); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, x13, x5, x9); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, x15, x7, (x9 & 0xffffffff00000000)); + out1[0] = x10; + out1[1] = x12; + out1[2] = x14; + out1[3] = x16; +} + +/// The function opp negates a field element in the Montgomery domain. +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = -eval (from_montgomery arg1) mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn opp(out1: *[4]u64, arg1: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + subborrowxU64(&x1, &x2, 0x0, cast(u64, 0x0), (arg1[0])); + var x3: u64 = undefined; + var x4: u1 = undefined; + subborrowxU64(&x3, &x4, x2, cast(u64, 0x0), (arg1[1])); + var x5: u64 = undefined; + var x6: u1 = undefined; + subborrowxU64(&x5, &x6, x4, cast(u64, 0x0), (arg1[2])); + var x7: u64 = undefined; + var x8: u1 = undefined; + subborrowxU64(&x7, &x8, x6, cast(u64, 0x0), (arg1[3])); + var x9: u64 = undefined; + cmovznzU64(&x9, x8, cast(u64, 0x0), 0xffffffffffffffff); + var x10: u64 = undefined; + var x11: u1 = undefined; + addcarryxU64(&x10, &x11, 0x0, x1, (x9 & 0xf3b9cac2fc632551)); + var x12: u64 = undefined; + var x13: u1 = undefined; + addcarryxU64(&x12, &x13, x11, x3, (x9 & 0xbce6faada7179e84)); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, x13, x5, x9); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, x15, x7, (x9 & 0xffffffff00000000)); + out1[0] = x10; + out1[1] = x12; + out1[2] = x14; + out1[3] = x16; +} + +/// The function fromMontgomery translates a field element out of the Montgomery domain. +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval out1 mod m = (eval arg1 * ((2^64)⁻¹ mod m)^4) mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn fromMontgomery(out1: *[4]u64, arg1: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[0]); + var x2: u64 = undefined; + var x3: u64 = undefined; + mulxU64(&x2, &x3, x1, 0xccd1c8aaee00bc4f); + var x4: u64 = undefined; + var x5: u64 = undefined; + mulxU64(&x4, &x5, x2, 0xffffffff00000000); + var x6: u64 = undefined; + var x7: u64 = undefined; + mulxU64(&x6, &x7, x2, 0xffffffffffffffff); + var x8: u64 = undefined; + var x9: u64 = undefined; + mulxU64(&x8, &x9, x2, 0xbce6faada7179e84); + var x10: u64 = undefined; + var x11: u64 = undefined; + mulxU64(&x10, &x11, x2, 0xf3b9cac2fc632551); + var x12: u64 = undefined; + var x13: u1 = undefined; + addcarryxU64(&x12, &x13, 0x0, x11, x8); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, x13, x9, x6); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, x15, x7, x4); + var x18: u64 = undefined; + var x19: u1 = undefined; + addcarryxU64(&x18, &x19, 0x0, x1, x10); + var x20: u64 = undefined; + var x21: u1 = undefined; + addcarryxU64(&x20, &x21, x19, cast(u64, 0x0), x12); + var x22: u64 = undefined; + var x23: u1 = undefined; + addcarryxU64(&x22, &x23, x21, cast(u64, 0x0), x14); + var x24: u64 = undefined; + var x25: u1 = undefined; + addcarryxU64(&x24, &x25, x23, cast(u64, 0x0), x16); + var x26: u64 = undefined; + var x27: u1 = undefined; + addcarryxU64(&x26, &x27, 0x0, x20, (arg1[1])); + var x28: u64 = undefined; + var x29: u1 = undefined; + addcarryxU64(&x28, &x29, x27, x22, cast(u64, 0x0)); + var x30: u64 = undefined; + var x31: u1 = undefined; + addcarryxU64(&x30, &x31, x29, x24, cast(u64, 0x0)); + var x32: u64 = undefined; + var x33: u64 = undefined; + mulxU64(&x32, &x33, x26, 0xccd1c8aaee00bc4f); + var x34: u64 = undefined; + var x35: u64 = undefined; + mulxU64(&x34, &x35, x32, 0xffffffff00000000); + var x36: u64 = undefined; + var x37: u64 = undefined; + mulxU64(&x36, &x37, x32, 0xffffffffffffffff); + var x38: u64 = undefined; + var x39: u64 = undefined; + mulxU64(&x38, &x39, x32, 0xbce6faada7179e84); + var x40: u64 = undefined; + var x41: u64 = undefined; + mulxU64(&x40, &x41, x32, 0xf3b9cac2fc632551); + var x42: u64 = undefined; + var x43: u1 = undefined; + addcarryxU64(&x42, &x43, 0x0, x41, x38); + var x44: u64 = undefined; + var x45: u1 = undefined; + addcarryxU64(&x44, &x45, x43, x39, x36); + var x46: u64 = undefined; + var x47: u1 = undefined; + addcarryxU64(&x46, &x47, x45, x37, x34); + var x48: u64 = undefined; + var x49: u1 = undefined; + addcarryxU64(&x48, &x49, 0x0, x26, x40); + var x50: u64 = undefined; + var x51: u1 = undefined; + addcarryxU64(&x50, &x51, x49, x28, x42); + var x52: u64 = undefined; + var x53: u1 = undefined; + addcarryxU64(&x52, &x53, x51, x30, x44); + var x54: u64 = undefined; + var x55: u1 = undefined; + addcarryxU64(&x54, &x55, x53, (cast(u64, x31) + (cast(u64, x25) + (cast(u64, x17) + x5))), x46); + var x56: u64 = undefined; + var x57: u1 = undefined; + addcarryxU64(&x56, &x57, 0x0, x50, (arg1[2])); + var x58: u64 = undefined; + var x59: u1 = undefined; + addcarryxU64(&x58, &x59, x57, x52, cast(u64, 0x0)); + var x60: u64 = undefined; + var x61: u1 = undefined; + addcarryxU64(&x60, &x61, x59, x54, cast(u64, 0x0)); + var x62: u64 = undefined; + var x63: u64 = undefined; + mulxU64(&x62, &x63, x56, 0xccd1c8aaee00bc4f); + var x64: u64 = undefined; + var x65: u64 = undefined; + mulxU64(&x64, &x65, x62, 0xffffffff00000000); + var x66: u64 = undefined; + var x67: u64 = undefined; + mulxU64(&x66, &x67, x62, 0xffffffffffffffff); + var x68: u64 = undefined; + var x69: u64 = undefined; + mulxU64(&x68, &x69, x62, 0xbce6faada7179e84); + var x70: u64 = undefined; + var x71: u64 = undefined; + mulxU64(&x70, &x71, x62, 0xf3b9cac2fc632551); + var x72: u64 = undefined; + var x73: u1 = undefined; + addcarryxU64(&x72, &x73, 0x0, x71, x68); + var x74: u64 = undefined; + var x75: u1 = undefined; + addcarryxU64(&x74, &x75, x73, x69, x66); + var x76: u64 = undefined; + var x77: u1 = undefined; + addcarryxU64(&x76, &x77, x75, x67, x64); + var x78: u64 = undefined; + var x79: u1 = undefined; + addcarryxU64(&x78, &x79, 0x0, x56, x70); + var x80: u64 = undefined; + var x81: u1 = undefined; + addcarryxU64(&x80, &x81, x79, x58, x72); + var x82: u64 = undefined; + var x83: u1 = undefined; + addcarryxU64(&x82, &x83, x81, x60, x74); + var x84: u64 = undefined; + var x85: u1 = undefined; + addcarryxU64(&x84, &x85, x83, (cast(u64, x61) + (cast(u64, x55) + (cast(u64, x47) + x35))), x76); + var x86: u64 = undefined; + var x87: u1 = undefined; + addcarryxU64(&x86, &x87, 0x0, x80, (arg1[3])); + var x88: u64 = undefined; + var x89: u1 = undefined; + addcarryxU64(&x88, &x89, x87, x82, cast(u64, 0x0)); + var x90: u64 = undefined; + var x91: u1 = undefined; + addcarryxU64(&x90, &x91, x89, x84, cast(u64, 0x0)); + var x92: u64 = undefined; + var x93: u64 = undefined; + mulxU64(&x92, &x93, x86, 0xccd1c8aaee00bc4f); + var x94: u64 = undefined; + var x95: u64 = undefined; + mulxU64(&x94, &x95, x92, 0xffffffff00000000); + var x96: u64 = undefined; + var x97: u64 = undefined; + mulxU64(&x96, &x97, x92, 0xffffffffffffffff); + var x98: u64 = undefined; + var x99: u64 = undefined; + mulxU64(&x98, &x99, x92, 0xbce6faada7179e84); + var x100: u64 = undefined; + var x101: u64 = undefined; + mulxU64(&x100, &x101, x92, 0xf3b9cac2fc632551); + var x102: u64 = undefined; + var x103: u1 = undefined; + addcarryxU64(&x102, &x103, 0x0, x101, x98); + var x104: u64 = undefined; + var x105: u1 = undefined; + addcarryxU64(&x104, &x105, x103, x99, x96); + var x106: u64 = undefined; + var x107: u1 = undefined; + addcarryxU64(&x106, &x107, x105, x97, x94); + var x108: u64 = undefined; + var x109: u1 = undefined; + addcarryxU64(&x108, &x109, 0x0, x86, x100); + var x110: u64 = undefined; + var x111: u1 = undefined; + addcarryxU64(&x110, &x111, x109, x88, x102); + var x112: u64 = undefined; + var x113: u1 = undefined; + addcarryxU64(&x112, &x113, x111, x90, x104); + var x114: u64 = undefined; + var x115: u1 = undefined; + addcarryxU64(&x114, &x115, x113, (cast(u64, x91) + (cast(u64, x85) + (cast(u64, x77) + x65))), x106); + const x116 = (cast(u64, x115) + (cast(u64, x107) + x95)); + var x117: u64 = undefined; + var x118: u1 = undefined; + subborrowxU64(&x117, &x118, 0x0, x110, 0xf3b9cac2fc632551); + var x119: u64 = undefined; + var x120: u1 = undefined; + subborrowxU64(&x119, &x120, x118, x112, 0xbce6faada7179e84); + var x121: u64 = undefined; + var x122: u1 = undefined; + subborrowxU64(&x121, &x122, x120, x114, 0xffffffffffffffff); + var x123: u64 = undefined; + var x124: u1 = undefined; + subborrowxU64(&x123, &x124, x122, x116, 0xffffffff00000000); + var x125: u64 = undefined; + var x126: u1 = undefined; + subborrowxU64(&x125, &x126, x124, cast(u64, 0x0), cast(u64, 0x0)); + var x127: u64 = undefined; + cmovznzU64(&x127, x126, x117, x110); + var x128: u64 = undefined; + cmovznzU64(&x128, x126, x119, x112); + var x129: u64 = undefined; + cmovznzU64(&x129, x126, x121, x114); + var x130: u64 = undefined; + cmovznzU64(&x130, x126, x123, x116); + out1[0] = x127; + out1[1] = x128; + out1[2] = x129; + out1[3] = x130; +} + +/// The function toMontgomery translates a field element into the Montgomery domain. +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = eval arg1 mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn toMontgomery(out1: *[4]u64, arg1: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[1]); + const x2 = (arg1[2]); + const x3 = (arg1[3]); + const x4 = (arg1[0]); + var x5: u64 = undefined; + var x6: u64 = undefined; + mulxU64(&x5, &x6, x4, 0x66e12d94f3d95620); + var x7: u64 = undefined; + var x8: u64 = undefined; + mulxU64(&x7, &x8, x4, 0x2845b2392b6bec59); + var x9: u64 = undefined; + var x10: u64 = undefined; + mulxU64(&x9, &x10, x4, 0x4699799c49bd6fa6); + var x11: u64 = undefined; + var x12: u64 = undefined; + mulxU64(&x11, &x12, x4, 0x83244c95be79eea2); + var x13: u64 = undefined; + var x14: u1 = undefined; + addcarryxU64(&x13, &x14, 0x0, x12, x9); + var x15: u64 = undefined; + var x16: u1 = undefined; + addcarryxU64(&x15, &x16, x14, x10, x7); + var x17: u64 = undefined; + var x18: u1 = undefined; + addcarryxU64(&x17, &x18, x16, x8, x5); + var x19: u64 = undefined; + var x20: u64 = undefined; + mulxU64(&x19, &x20, x11, 0xccd1c8aaee00bc4f); + var x21: u64 = undefined; + var x22: u64 = undefined; + mulxU64(&x21, &x22, x19, 0xffffffff00000000); + var x23: u64 = undefined; + var x24: u64 = undefined; + mulxU64(&x23, &x24, x19, 0xffffffffffffffff); + var x25: u64 = undefined; + var x26: u64 = undefined; + mulxU64(&x25, &x26, x19, 0xbce6faada7179e84); + var x27: u64 = undefined; + var x28: u64 = undefined; + mulxU64(&x27, &x28, x19, 0xf3b9cac2fc632551); + var x29: u64 = undefined; + var x30: u1 = undefined; + addcarryxU64(&x29, &x30, 0x0, x28, x25); + var x31: u64 = undefined; + var x32: u1 = undefined; + addcarryxU64(&x31, &x32, x30, x26, x23); + var x33: u64 = undefined; + var x34: u1 = undefined; + addcarryxU64(&x33, &x34, x32, x24, x21); + var x35: u64 = undefined; + var x36: u1 = undefined; + addcarryxU64(&x35, &x36, 0x0, x11, x27); + var x37: u64 = undefined; + var x38: u1 = undefined; + addcarryxU64(&x37, &x38, x36, x13, x29); + var x39: u64 = undefined; + var x40: u1 = undefined; + addcarryxU64(&x39, &x40, x38, x15, x31); + var x41: u64 = undefined; + var x42: u1 = undefined; + addcarryxU64(&x41, &x42, x40, x17, x33); + var x43: u64 = undefined; + var x44: u1 = undefined; + addcarryxU64(&x43, &x44, x42, (cast(u64, x18) + x6), (cast(u64, x34) + x22)); + var x45: u64 = undefined; + var x46: u64 = undefined; + mulxU64(&x45, &x46, x1, 0x66e12d94f3d95620); + var x47: u64 = undefined; + var x48: u64 = undefined; + mulxU64(&x47, &x48, x1, 0x2845b2392b6bec59); + var x49: u64 = undefined; + var x50: u64 = undefined; + mulxU64(&x49, &x50, x1, 0x4699799c49bd6fa6); + var x51: u64 = undefined; + var x52: u64 = undefined; + mulxU64(&x51, &x52, x1, 0x83244c95be79eea2); + var x53: u64 = undefined; + var x54: u1 = undefined; + addcarryxU64(&x53, &x54, 0x0, x52, x49); + var x55: u64 = undefined; + var x56: u1 = undefined; + addcarryxU64(&x55, &x56, x54, x50, x47); + var x57: u64 = undefined; + var x58: u1 = undefined; + addcarryxU64(&x57, &x58, x56, x48, x45); + var x59: u64 = undefined; + var x60: u1 = undefined; + addcarryxU64(&x59, &x60, 0x0, x37, x51); + var x61: u64 = undefined; + var x62: u1 = undefined; + addcarryxU64(&x61, &x62, x60, x39, x53); + var x63: u64 = undefined; + var x64: u1 = undefined; + addcarryxU64(&x63, &x64, x62, x41, x55); + var x65: u64 = undefined; + var x66: u1 = undefined; + addcarryxU64(&x65, &x66, x64, x43, x57); + var x67: u64 = undefined; + var x68: u64 = undefined; + mulxU64(&x67, &x68, x59, 0xccd1c8aaee00bc4f); + var x69: u64 = undefined; + var x70: u64 = undefined; + mulxU64(&x69, &x70, x67, 0xffffffff00000000); + var x71: u64 = undefined; + var x72: u64 = undefined; + mulxU64(&x71, &x72, x67, 0xffffffffffffffff); + var x73: u64 = undefined; + var x74: u64 = undefined; + mulxU64(&x73, &x74, x67, 0xbce6faada7179e84); + var x75: u64 = undefined; + var x76: u64 = undefined; + mulxU64(&x75, &x76, x67, 0xf3b9cac2fc632551); + var x77: u64 = undefined; + var x78: u1 = undefined; + addcarryxU64(&x77, &x78, 0x0, x76, x73); + var x79: u64 = undefined; + var x80: u1 = undefined; + addcarryxU64(&x79, &x80, x78, x74, x71); + var x81: u64 = undefined; + var x82: u1 = undefined; + addcarryxU64(&x81, &x82, x80, x72, x69); + var x83: u64 = undefined; + var x84: u1 = undefined; + addcarryxU64(&x83, &x84, 0x0, x59, x75); + var x85: u64 = undefined; + var x86: u1 = undefined; + addcarryxU64(&x85, &x86, x84, x61, x77); + var x87: u64 = undefined; + var x88: u1 = undefined; + addcarryxU64(&x87, &x88, x86, x63, x79); + var x89: u64 = undefined; + var x90: u1 = undefined; + addcarryxU64(&x89, &x90, x88, x65, x81); + var x91: u64 = undefined; + var x92: u1 = undefined; + addcarryxU64(&x91, &x92, x90, ((cast(u64, x66) + cast(u64, x44)) + (cast(u64, x58) + x46)), (cast(u64, x82) + x70)); + var x93: u64 = undefined; + var x94: u64 = undefined; + mulxU64(&x93, &x94, x2, 0x66e12d94f3d95620); + var x95: u64 = undefined; + var x96: u64 = undefined; + mulxU64(&x95, &x96, x2, 0x2845b2392b6bec59); + var x97: u64 = undefined; + var x98: u64 = undefined; + mulxU64(&x97, &x98, x2, 0x4699799c49bd6fa6); + var x99: u64 = undefined; + var x100: u64 = undefined; + mulxU64(&x99, &x100, x2, 0x83244c95be79eea2); + var x101: u64 = undefined; + var x102: u1 = undefined; + addcarryxU64(&x101, &x102, 0x0, x100, x97); + var x103: u64 = undefined; + var x104: u1 = undefined; + addcarryxU64(&x103, &x104, x102, x98, x95); + var x105: u64 = undefined; + var x106: u1 = undefined; + addcarryxU64(&x105, &x106, x104, x96, x93); + var x107: u64 = undefined; + var x108: u1 = undefined; + addcarryxU64(&x107, &x108, 0x0, x85, x99); + var x109: u64 = undefined; + var x110: u1 = undefined; + addcarryxU64(&x109, &x110, x108, x87, x101); + var x111: u64 = undefined; + var x112: u1 = undefined; + addcarryxU64(&x111, &x112, x110, x89, x103); + var x113: u64 = undefined; + var x114: u1 = undefined; + addcarryxU64(&x113, &x114, x112, x91, x105); + var x115: u64 = undefined; + var x116: u64 = undefined; + mulxU64(&x115, &x116, x107, 0xccd1c8aaee00bc4f); + var x117: u64 = undefined; + var x118: u64 = undefined; + mulxU64(&x117, &x118, x115, 0xffffffff00000000); + var x119: u64 = undefined; + var x120: u64 = undefined; + mulxU64(&x119, &x120, x115, 0xffffffffffffffff); + var x121: u64 = undefined; + var x122: u64 = undefined; + mulxU64(&x121, &x122, x115, 0xbce6faada7179e84); + var x123: u64 = undefined; + var x124: u64 = undefined; + mulxU64(&x123, &x124, x115, 0xf3b9cac2fc632551); + var x125: u64 = undefined; + var x126: u1 = undefined; + addcarryxU64(&x125, &x126, 0x0, x124, x121); + var x127: u64 = undefined; + var x128: u1 = undefined; + addcarryxU64(&x127, &x128, x126, x122, x119); + var x129: u64 = undefined; + var x130: u1 = undefined; + addcarryxU64(&x129, &x130, x128, x120, x117); + var x131: u64 = undefined; + var x132: u1 = undefined; + addcarryxU64(&x131, &x132, 0x0, x107, x123); + var x133: u64 = undefined; + var x134: u1 = undefined; + addcarryxU64(&x133, &x134, x132, x109, x125); + var x135: u64 = undefined; + var x136: u1 = undefined; + addcarryxU64(&x135, &x136, x134, x111, x127); + var x137: u64 = undefined; + var x138: u1 = undefined; + addcarryxU64(&x137, &x138, x136, x113, x129); + var x139: u64 = undefined; + var x140: u1 = undefined; + addcarryxU64(&x139, &x140, x138, ((cast(u64, x114) + cast(u64, x92)) + (cast(u64, x106) + x94)), (cast(u64, x130) + x118)); + var x141: u64 = undefined; + var x142: u64 = undefined; + mulxU64(&x141, &x142, x3, 0x66e12d94f3d95620); + var x143: u64 = undefined; + var x144: u64 = undefined; + mulxU64(&x143, &x144, x3, 0x2845b2392b6bec59); + var x145: u64 = undefined; + var x146: u64 = undefined; + mulxU64(&x145, &x146, x3, 0x4699799c49bd6fa6); + var x147: u64 = undefined; + var x148: u64 = undefined; + mulxU64(&x147, &x148, x3, 0x83244c95be79eea2); + var x149: u64 = undefined; + var x150: u1 = undefined; + addcarryxU64(&x149, &x150, 0x0, x148, x145); + var x151: u64 = undefined; + var x152: u1 = undefined; + addcarryxU64(&x151, &x152, x150, x146, x143); + var x153: u64 = undefined; + var x154: u1 = undefined; + addcarryxU64(&x153, &x154, x152, x144, x141); + var x155: u64 = undefined; + var x156: u1 = undefined; + addcarryxU64(&x155, &x156, 0x0, x133, x147); + var x157: u64 = undefined; + var x158: u1 = undefined; + addcarryxU64(&x157, &x158, x156, x135, x149); + var x159: u64 = undefined; + var x160: u1 = undefined; + addcarryxU64(&x159, &x160, x158, x137, x151); + var x161: u64 = undefined; + var x162: u1 = undefined; + addcarryxU64(&x161, &x162, x160, x139, x153); + var x163: u64 = undefined; + var x164: u64 = undefined; + mulxU64(&x163, &x164, x155, 0xccd1c8aaee00bc4f); + var x165: u64 = undefined; + var x166: u64 = undefined; + mulxU64(&x165, &x166, x163, 0xffffffff00000000); + var x167: u64 = undefined; + var x168: u64 = undefined; + mulxU64(&x167, &x168, x163, 0xffffffffffffffff); + var x169: u64 = undefined; + var x170: u64 = undefined; + mulxU64(&x169, &x170, x163, 0xbce6faada7179e84); + var x171: u64 = undefined; + var x172: u64 = undefined; + mulxU64(&x171, &x172, x163, 0xf3b9cac2fc632551); + var x173: u64 = undefined; + var x174: u1 = undefined; + addcarryxU64(&x173, &x174, 0x0, x172, x169); + var x175: u64 = undefined; + var x176: u1 = undefined; + addcarryxU64(&x175, &x176, x174, x170, x167); + var x177: u64 = undefined; + var x178: u1 = undefined; + addcarryxU64(&x177, &x178, x176, x168, x165); + var x179: u64 = undefined; + var x180: u1 = undefined; + addcarryxU64(&x179, &x180, 0x0, x155, x171); + var x181: u64 = undefined; + var x182: u1 = undefined; + addcarryxU64(&x181, &x182, x180, x157, x173); + var x183: u64 = undefined; + var x184: u1 = undefined; + addcarryxU64(&x183, &x184, x182, x159, x175); + var x185: u64 = undefined; + var x186: u1 = undefined; + addcarryxU64(&x185, &x186, x184, x161, x177); + var x187: u64 = undefined; + var x188: u1 = undefined; + addcarryxU64(&x187, &x188, x186, ((cast(u64, x162) + cast(u64, x140)) + (cast(u64, x154) + x142)), (cast(u64, x178) + x166)); + var x189: u64 = undefined; + var x190: u1 = undefined; + subborrowxU64(&x189, &x190, 0x0, x181, 0xf3b9cac2fc632551); + var x191: u64 = undefined; + var x192: u1 = undefined; + subborrowxU64(&x191, &x192, x190, x183, 0xbce6faada7179e84); + var x193: u64 = undefined; + var x194: u1 = undefined; + subborrowxU64(&x193, &x194, x192, x185, 0xffffffffffffffff); + var x195: u64 = undefined; + var x196: u1 = undefined; + subborrowxU64(&x195, &x196, x194, x187, 0xffffffff00000000); + var x197: u64 = undefined; + var x198: u1 = undefined; + subborrowxU64(&x197, &x198, x196, cast(u64, x188), cast(u64, 0x0)); + var x199: u64 = undefined; + cmovznzU64(&x199, x198, x189, x181); + var x200: u64 = undefined; + cmovznzU64(&x200, x198, x191, x183); + var x201: u64 = undefined; + cmovznzU64(&x201, x198, x193, x185); + var x202: u64 = undefined; + cmovznzU64(&x202, x198, x195, x187); + out1[0] = x199; + out1[1] = x200; + out1[2] = x201; + out1[3] = x202; +} + +/// The function nonzero outputs a single non-zero word if the input is non-zero and zero otherwise. +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// out1 = 0 ↔ eval (from_montgomery arg1) mod m = 0 +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +pub fn nonzero(out1: *u64, arg1: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = ((arg1[0]) | ((arg1[1]) | ((arg1[2]) | (arg1[3])))); + out1.* = x1; +} + +/// The function selectznz is a multi-limb conditional select. +/// Postconditions: +/// eval out1 = (if arg1 = 0 then eval arg2 else eval arg3) +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn selectznz(out1: *[4]u64, arg1: u1, arg2: [4]u64, arg3: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + cmovznzU64(&x1, arg1, (arg2[0]), (arg3[0])); + var x2: u64 = undefined; + cmovznzU64(&x2, arg1, (arg2[1]), (arg3[1])); + var x3: u64 = undefined; + cmovznzU64(&x3, arg1, (arg2[2]), (arg3[2])); + var x4: u64 = undefined; + cmovznzU64(&x4, arg1, (arg2[3]), (arg3[3])); + out1[0] = x1; + out1[1] = x2; + out1[2] = x3; + out1[3] = x4; +} + +/// The function toBytes serializes a field element NOT in the Montgomery domain to bytes in little-endian order. +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// out1 = map (λ x, ⌊((eval arg1 mod m) mod 2^(8 * (x + 1))) / 2^(8 * x)⌋) [0..31] +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff]] +pub fn toBytes(out1: *[32]u8, arg1: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[3]); + const x2 = (arg1[2]); + const x3 = (arg1[1]); + const x4 = (arg1[0]); + const x5 = cast(u8, (x4 & cast(u64, 0xff))); + const x6 = (x4 >> 8); + const x7 = cast(u8, (x6 & cast(u64, 0xff))); + const x8 = (x6 >> 8); + const x9 = cast(u8, (x8 & cast(u64, 0xff))); + const x10 = (x8 >> 8); + const x11 = cast(u8, (x10 & cast(u64, 0xff))); + const x12 = (x10 >> 8); + const x13 = cast(u8, (x12 & cast(u64, 0xff))); + const x14 = (x12 >> 8); + const x15 = cast(u8, (x14 & cast(u64, 0xff))); + const x16 = (x14 >> 8); + const x17 = cast(u8, (x16 & cast(u64, 0xff))); + const x18 = cast(u8, (x16 >> 8)); + const x19 = cast(u8, (x3 & cast(u64, 0xff))); + const x20 = (x3 >> 8); + const x21 = cast(u8, (x20 & cast(u64, 0xff))); + const x22 = (x20 >> 8); + const x23 = cast(u8, (x22 & cast(u64, 0xff))); + const x24 = (x22 >> 8); + const x25 = cast(u8, (x24 & cast(u64, 0xff))); + const x26 = (x24 >> 8); + const x27 = cast(u8, (x26 & cast(u64, 0xff))); + const x28 = (x26 >> 8); + const x29 = cast(u8, (x28 & cast(u64, 0xff))); + const x30 = (x28 >> 8); + const x31 = cast(u8, (x30 & cast(u64, 0xff))); + const x32 = cast(u8, (x30 >> 8)); + const x33 = cast(u8, (x2 & cast(u64, 0xff))); + const x34 = (x2 >> 8); + const x35 = cast(u8, (x34 & cast(u64, 0xff))); + const x36 = (x34 >> 8); + const x37 = cast(u8, (x36 & cast(u64, 0xff))); + const x38 = (x36 >> 8); + const x39 = cast(u8, (x38 & cast(u64, 0xff))); + const x40 = (x38 >> 8); + const x41 = cast(u8, (x40 & cast(u64, 0xff))); + const x42 = (x40 >> 8); + const x43 = cast(u8, (x42 & cast(u64, 0xff))); + const x44 = (x42 >> 8); + const x45 = cast(u8, (x44 & cast(u64, 0xff))); + const x46 = cast(u8, (x44 >> 8)); + const x47 = cast(u8, (x1 & cast(u64, 0xff))); + const x48 = (x1 >> 8); + const x49 = cast(u8, (x48 & cast(u64, 0xff))); + const x50 = (x48 >> 8); + const x51 = cast(u8, (x50 & cast(u64, 0xff))); + const x52 = (x50 >> 8); + const x53 = cast(u8, (x52 & cast(u64, 0xff))); + const x54 = (x52 >> 8); + const x55 = cast(u8, (x54 & cast(u64, 0xff))); + const x56 = (x54 >> 8); + const x57 = cast(u8, (x56 & cast(u64, 0xff))); + const x58 = (x56 >> 8); + const x59 = cast(u8, (x58 & cast(u64, 0xff))); + const x60 = cast(u8, (x58 >> 8)); + out1[0] = x5; + out1[1] = x7; + out1[2] = x9; + out1[3] = x11; + out1[4] = x13; + out1[5] = x15; + out1[6] = x17; + out1[7] = x18; + out1[8] = x19; + out1[9] = x21; + out1[10] = x23; + out1[11] = x25; + out1[12] = x27; + out1[13] = x29; + out1[14] = x31; + out1[15] = x32; + out1[16] = x33; + out1[17] = x35; + out1[18] = x37; + out1[19] = x39; + out1[20] = x41; + out1[21] = x43; + out1[22] = x45; + out1[23] = x46; + out1[24] = x47; + out1[25] = x49; + out1[26] = x51; + out1[27] = x53; + out1[28] = x55; + out1[29] = x57; + out1[30] = x59; + out1[31] = x60; +} + +/// The function fromBytes deserializes a field element NOT in the Montgomery domain from bytes in little-endian order. +/// Preconditions: +/// 0 ≤ bytes_eval arg1 < m +/// Postconditions: +/// eval out1 mod m = bytes_eval arg1 mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn fromBytes(out1: *[4]u64, arg1: [32]u8) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (cast(u64, (arg1[31])) << 56); + const x2 = (cast(u64, (arg1[30])) << 48); + const x3 = (cast(u64, (arg1[29])) << 40); + const x4 = (cast(u64, (arg1[28])) << 32); + const x5 = (cast(u64, (arg1[27])) << 24); + const x6 = (cast(u64, (arg1[26])) << 16); + const x7 = (cast(u64, (arg1[25])) << 8); + const x8 = (arg1[24]); + const x9 = (cast(u64, (arg1[23])) << 56); + const x10 = (cast(u64, (arg1[22])) << 48); + const x11 = (cast(u64, (arg1[21])) << 40); + const x12 = (cast(u64, (arg1[20])) << 32); + const x13 = (cast(u64, (arg1[19])) << 24); + const x14 = (cast(u64, (arg1[18])) << 16); + const x15 = (cast(u64, (arg1[17])) << 8); + const x16 = (arg1[16]); + const x17 = (cast(u64, (arg1[15])) << 56); + const x18 = (cast(u64, (arg1[14])) << 48); + const x19 = (cast(u64, (arg1[13])) << 40); + const x20 = (cast(u64, (arg1[12])) << 32); + const x21 = (cast(u64, (arg1[11])) << 24); + const x22 = (cast(u64, (arg1[10])) << 16); + const x23 = (cast(u64, (arg1[9])) << 8); + const x24 = (arg1[8]); + const x25 = (cast(u64, (arg1[7])) << 56); + const x26 = (cast(u64, (arg1[6])) << 48); + const x27 = (cast(u64, (arg1[5])) << 40); + const x28 = (cast(u64, (arg1[4])) << 32); + const x29 = (cast(u64, (arg1[3])) << 24); + const x30 = (cast(u64, (arg1[2])) << 16); + const x31 = (cast(u64, (arg1[1])) << 8); + const x32 = (arg1[0]); + const x33 = (x31 + cast(u64, x32)); + const x34 = (x30 + x33); + const x35 = (x29 + x34); + const x36 = (x28 + x35); + const x37 = (x27 + x36); + const x38 = (x26 + x37); + const x39 = (x25 + x38); + const x40 = (x23 + cast(u64, x24)); + const x41 = (x22 + x40); + const x42 = (x21 + x41); + const x43 = (x20 + x42); + const x44 = (x19 + x43); + const x45 = (x18 + x44); + const x46 = (x17 + x45); + const x47 = (x15 + cast(u64, x16)); + const x48 = (x14 + x47); + const x49 = (x13 + x48); + const x50 = (x12 + x49); + const x51 = (x11 + x50); + const x52 = (x10 + x51); + const x53 = (x9 + x52); + const x54 = (x7 + cast(u64, x8)); + const x55 = (x6 + x54); + const x56 = (x5 + x55); + const x57 = (x4 + x56); + const x58 = (x3 + x57); + const x59 = (x2 + x58); + const x60 = (x1 + x59); + out1[0] = x39; + out1[1] = x46; + out1[2] = x53; + out1[3] = x60; +} + +/// The function setOne returns the field element one in the Montgomery domain. +/// Postconditions: +/// eval (from_montgomery out1) mod m = 1 mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn setOne(out1: *[4]u64) void { + @setRuntimeSafety(mode == .Debug); + + out1[0] = 0xc46353d039cdaaf; + out1[1] = 0x4319055258e8617b; + out1[2] = cast(u64, 0x0); + out1[3] = 0xffffffff; +} + +/// The function msat returns the saturated representation of the prime modulus. +/// Postconditions: +/// twos_complement_eval out1 = m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn msat(out1: *[5]u64) void { + @setRuntimeSafety(mode == .Debug); + + out1[0] = 0xf3b9cac2fc632551; + out1[1] = 0xbce6faada7179e84; + out1[2] = 0xffffffffffffffff; + out1[3] = 0xffffffff00000000; + out1[4] = cast(u64, 0x0); +} + +/// The function divstepPrecomp returns the precomputed value for Bernstein-Yang-inversion (in montgomery form). +/// Postconditions: +/// eval (from_montgomery out1) = ⌊(m - 1) / 2⌋^(if (log2 m) + 1 < 46 then ⌊(49 * ((log2 m) + 1) + 80) / 17⌋ else ⌊(49 * ((log2 m) + 1) + 57) / 17⌋) +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn divstepPrecomp(out1: *[4]u64) void { + @setRuntimeSafety(mode == .Debug); + + out1[0] = 0xd739262fb7fcfbb5; + out1[1] = 0x8ac6f75d20074414; + out1[2] = 0xc67428bfb5e3c256; + out1[3] = 0x444962f2eda7aedf; +} + +/// The function divstep computes a divstep. +/// Preconditions: +/// 0 ≤ eval arg4 < m +/// 0 ≤ eval arg5 < m +/// Postconditions: +/// out1 = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then 1 - arg1 else 1 + arg1) +/// twos_complement_eval out2 = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then twos_complement_eval arg3 else twos_complement_eval arg2) +/// twos_complement_eval out3 = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then ⌊(twos_complement_eval arg3 - twos_complement_eval arg2) / 2⌋ else ⌊(twos_complement_eval arg3 + (twos_complement_eval arg3 mod 2) * twos_complement_eval arg2) / 2⌋) +/// eval (from_montgomery out4) mod m = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then (2 * eval (from_montgomery arg5)) mod m else (2 * eval (from_montgomery arg4)) mod m) +/// eval (from_montgomery out5) mod m = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then (eval (from_montgomery arg4) - eval (from_montgomery arg4)) mod m else (eval (from_montgomery arg5) + (twos_complement_eval arg3 mod 2) * eval (from_montgomery arg4)) mod m) +/// 0 ≤ eval out5 < m +/// 0 ≤ eval out5 < m +/// 0 ≤ eval out2 < m +/// 0 ≤ eval out3 < m +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0xffffffffffffffff] +/// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg4: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg5: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// out3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// out4: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// out5: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn divstep(out1: *u64, out2: *[5]u64, out3: *[5]u64, out4: *[4]u64, out5: *[4]u64, arg1: u64, arg2: [5]u64, arg3: [5]u64, arg4: [4]u64, arg5: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + addcarryxU64(&x1, &x2, 0x0, (~arg1), cast(u64, 0x1)); + const x3 = (cast(u1, (x1 >> 63)) & cast(u1, ((arg3[0]) & cast(u64, 0x1)))); + var x4: u64 = undefined; + var x5: u1 = undefined; + addcarryxU64(&x4, &x5, 0x0, (~arg1), cast(u64, 0x1)); + var x6: u64 = undefined; + cmovznzU64(&x6, x3, arg1, x4); + var x7: u64 = undefined; + cmovznzU64(&x7, x3, (arg2[0]), (arg3[0])); + var x8: u64 = undefined; + cmovznzU64(&x8, x3, (arg2[1]), (arg3[1])); + var x9: u64 = undefined; + cmovznzU64(&x9, x3, (arg2[2]), (arg3[2])); + var x10: u64 = undefined; + cmovznzU64(&x10, x3, (arg2[3]), (arg3[3])); + var x11: u64 = undefined; + cmovznzU64(&x11, x3, (arg2[4]), (arg3[4])); + var x12: u64 = undefined; + var x13: u1 = undefined; + addcarryxU64(&x12, &x13, 0x0, cast(u64, 0x1), (~(arg2[0]))); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, x13, cast(u64, 0x0), (~(arg2[1]))); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, x15, cast(u64, 0x0), (~(arg2[2]))); + var x18: u64 = undefined; + var x19: u1 = undefined; + addcarryxU64(&x18, &x19, x17, cast(u64, 0x0), (~(arg2[3]))); + var x20: u64 = undefined; + var x21: u1 = undefined; + addcarryxU64(&x20, &x21, x19, cast(u64, 0x0), (~(arg2[4]))); + var x22: u64 = undefined; + cmovznzU64(&x22, x3, (arg3[0]), x12); + var x23: u64 = undefined; + cmovznzU64(&x23, x3, (arg3[1]), x14); + var x24: u64 = undefined; + cmovznzU64(&x24, x3, (arg3[2]), x16); + var x25: u64 = undefined; + cmovznzU64(&x25, x3, (arg3[3]), x18); + var x26: u64 = undefined; + cmovznzU64(&x26, x3, (arg3[4]), x20); + var x27: u64 = undefined; + cmovznzU64(&x27, x3, (arg4[0]), (arg5[0])); + var x28: u64 = undefined; + cmovznzU64(&x28, x3, (arg4[1]), (arg5[1])); + var x29: u64 = undefined; + cmovznzU64(&x29, x3, (arg4[2]), (arg5[2])); + var x30: u64 = undefined; + cmovznzU64(&x30, x3, (arg4[3]), (arg5[3])); + var x31: u64 = undefined; + var x32: u1 = undefined; + addcarryxU64(&x31, &x32, 0x0, x27, x27); + var x33: u64 = undefined; + var x34: u1 = undefined; + addcarryxU64(&x33, &x34, x32, x28, x28); + var x35: u64 = undefined; + var x36: u1 = undefined; + addcarryxU64(&x35, &x36, x34, x29, x29); + var x37: u64 = undefined; + var x38: u1 = undefined; + addcarryxU64(&x37, &x38, x36, x30, x30); + var x39: u64 = undefined; + var x40: u1 = undefined; + subborrowxU64(&x39, &x40, 0x0, x31, 0xf3b9cac2fc632551); + var x41: u64 = undefined; + var x42: u1 = undefined; + subborrowxU64(&x41, &x42, x40, x33, 0xbce6faada7179e84); + var x43: u64 = undefined; + var x44: u1 = undefined; + subborrowxU64(&x43, &x44, x42, x35, 0xffffffffffffffff); + var x45: u64 = undefined; + var x46: u1 = undefined; + subborrowxU64(&x45, &x46, x44, x37, 0xffffffff00000000); + var x47: u64 = undefined; + var x48: u1 = undefined; + subborrowxU64(&x47, &x48, x46, cast(u64, x38), cast(u64, 0x0)); + const x49 = (arg4[3]); + const x50 = (arg4[2]); + const x51 = (arg4[1]); + const x52 = (arg4[0]); + var x53: u64 = undefined; + var x54: u1 = undefined; + subborrowxU64(&x53, &x54, 0x0, cast(u64, 0x0), x52); + var x55: u64 = undefined; + var x56: u1 = undefined; + subborrowxU64(&x55, &x56, x54, cast(u64, 0x0), x51); + var x57: u64 = undefined; + var x58: u1 = undefined; + subborrowxU64(&x57, &x58, x56, cast(u64, 0x0), x50); + var x59: u64 = undefined; + var x60: u1 = undefined; + subborrowxU64(&x59, &x60, x58, cast(u64, 0x0), x49); + var x61: u64 = undefined; + cmovznzU64(&x61, x60, cast(u64, 0x0), 0xffffffffffffffff); + var x62: u64 = undefined; + var x63: u1 = undefined; + addcarryxU64(&x62, &x63, 0x0, x53, (x61 & 0xf3b9cac2fc632551)); + var x64: u64 = undefined; + var x65: u1 = undefined; + addcarryxU64(&x64, &x65, x63, x55, (x61 & 0xbce6faada7179e84)); + var x66: u64 = undefined; + var x67: u1 = undefined; + addcarryxU64(&x66, &x67, x65, x57, x61); + var x68: u64 = undefined; + var x69: u1 = undefined; + addcarryxU64(&x68, &x69, x67, x59, (x61 & 0xffffffff00000000)); + var x70: u64 = undefined; + cmovznzU64(&x70, x3, (arg5[0]), x62); + var x71: u64 = undefined; + cmovznzU64(&x71, x3, (arg5[1]), x64); + var x72: u64 = undefined; + cmovznzU64(&x72, x3, (arg5[2]), x66); + var x73: u64 = undefined; + cmovznzU64(&x73, x3, (arg5[3]), x68); + const x74 = cast(u1, (x22 & cast(u64, 0x1))); + var x75: u64 = undefined; + cmovznzU64(&x75, x74, cast(u64, 0x0), x7); + var x76: u64 = undefined; + cmovznzU64(&x76, x74, cast(u64, 0x0), x8); + var x77: u64 = undefined; + cmovznzU64(&x77, x74, cast(u64, 0x0), x9); + var x78: u64 = undefined; + cmovznzU64(&x78, x74, cast(u64, 0x0), x10); + var x79: u64 = undefined; + cmovznzU64(&x79, x74, cast(u64, 0x0), x11); + var x80: u64 = undefined; + var x81: u1 = undefined; + addcarryxU64(&x80, &x81, 0x0, x22, x75); + var x82: u64 = undefined; + var x83: u1 = undefined; + addcarryxU64(&x82, &x83, x81, x23, x76); + var x84: u64 = undefined; + var x85: u1 = undefined; + addcarryxU64(&x84, &x85, x83, x24, x77); + var x86: u64 = undefined; + var x87: u1 = undefined; + addcarryxU64(&x86, &x87, x85, x25, x78); + var x88: u64 = undefined; + var x89: u1 = undefined; + addcarryxU64(&x88, &x89, x87, x26, x79); + var x90: u64 = undefined; + cmovznzU64(&x90, x74, cast(u64, 0x0), x27); + var x91: u64 = undefined; + cmovznzU64(&x91, x74, cast(u64, 0x0), x28); + var x92: u64 = undefined; + cmovznzU64(&x92, x74, cast(u64, 0x0), x29); + var x93: u64 = undefined; + cmovznzU64(&x93, x74, cast(u64, 0x0), x30); + var x94: u64 = undefined; + var x95: u1 = undefined; + addcarryxU64(&x94, &x95, 0x0, x70, x90); + var x96: u64 = undefined; + var x97: u1 = undefined; + addcarryxU64(&x96, &x97, x95, x71, x91); + var x98: u64 = undefined; + var x99: u1 = undefined; + addcarryxU64(&x98, &x99, x97, x72, x92); + var x100: u64 = undefined; + var x101: u1 = undefined; + addcarryxU64(&x100, &x101, x99, x73, x93); + var x102: u64 = undefined; + var x103: u1 = undefined; + subborrowxU64(&x102, &x103, 0x0, x94, 0xf3b9cac2fc632551); + var x104: u64 = undefined; + var x105: u1 = undefined; + subborrowxU64(&x104, &x105, x103, x96, 0xbce6faada7179e84); + var x106: u64 = undefined; + var x107: u1 = undefined; + subborrowxU64(&x106, &x107, x105, x98, 0xffffffffffffffff); + var x108: u64 = undefined; + var x109: u1 = undefined; + subborrowxU64(&x108, &x109, x107, x100, 0xffffffff00000000); + var x110: u64 = undefined; + var x111: u1 = undefined; + subborrowxU64(&x110, &x111, x109, cast(u64, x101), cast(u64, 0x0)); + var x112: u64 = undefined; + var x113: u1 = undefined; + addcarryxU64(&x112, &x113, 0x0, x6, cast(u64, 0x1)); + const x114 = ((x80 >> 1) | ((x82 << 63) & 0xffffffffffffffff)); + const x115 = ((x82 >> 1) | ((x84 << 63) & 0xffffffffffffffff)); + const x116 = ((x84 >> 1) | ((x86 << 63) & 0xffffffffffffffff)); + const x117 = ((x86 >> 1) | ((x88 << 63) & 0xffffffffffffffff)); + const x118 = ((x88 & 0x8000000000000000) | (x88 >> 1)); + var x119: u64 = undefined; + cmovznzU64(&x119, x48, x39, x31); + var x120: u64 = undefined; + cmovznzU64(&x120, x48, x41, x33); + var x121: u64 = undefined; + cmovznzU64(&x121, x48, x43, x35); + var x122: u64 = undefined; + cmovznzU64(&x122, x48, x45, x37); + var x123: u64 = undefined; + cmovznzU64(&x123, x111, x102, x94); + var x124: u64 = undefined; + cmovznzU64(&x124, x111, x104, x96); + var x125: u64 = undefined; + cmovznzU64(&x125, x111, x106, x98); + var x126: u64 = undefined; + cmovznzU64(&x126, x111, x108, x100); + out1.* = x112; + out2[0] = x7; + out2[1] = x8; + out2[2] = x9; + out2[3] = x10; + out2[4] = x11; + out3[0] = x114; + out3[1] = x115; + out3[2] = x116; + out3[3] = x117; + out3[4] = x118; + out4[0] = x119; + out4[1] = x120; + out4[2] = x121; + out4[3] = x122; + out5[0] = x123; + out5[1] = x124; + out5[2] = x125; + out5[3] = x126; +} diff --git a/zig/lib/std/crypto/pcurves/p256/scalar.zig b/zig/lib/std/crypto/pcurves/p256/scalar.zig new file mode 100644 index 0000000000..3cd6689897 --- /dev/null +++ b/zig/lib/std/crypto/pcurves/p256/scalar.zig @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2021 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. + +const std = @import("std"); +const builtin = std.builtin; +const common = @import("../common.zig"); +const crypto = std.crypto; +const debug = std.debug; +const math = std.math; +const mem = std.mem; + +const Field = common.Field; + +const NonCanonicalError = std.crypto.errors.NonCanonicalError; +const NotSquareError = std.crypto.errors.NotSquareError; + +/// Number of bytes required to encode a scalar. +pub const encoded_length = 32; + +/// A compressed scalar, in canonical form. +pub const CompressedScalar = [encoded_length]u8; + +const Fe = Field(.{ + .fiat = @import("p256_scalar_64.zig"), + .field_order = 115792089210356248762697446949407573529996955224135760342422259061068512044369, + .field_bits = 256, + .saturated_bits = 255, + .encoded_length = encoded_length, +}); + +/// Reject a scalar whose encoding is not canonical. +pub fn rejectNonCanonical(s: CompressedScalar, endian: builtin.Endian) NonCanonicalError!void { + return Fe.rejectNonCanonical(s, endian); +} + +/// Reduce a 48-bytes scalar to the field size. +pub fn reduce48(s: [48]u8, endian: builtin.Endian) CompressedScalar { + return Scalar.fromBytes48(s, endian).toBytes(endian); +} + +/// Reduce a 64-bytes scalar to the field size. +pub fn reduce64(s: [64]u8, endian: builtin.Endian) CompressedScalar { + return ScalarDouble.fromBytes64(s, endian).toBytes(endian); +} + +/// Return a*b (mod L) +pub fn mul(a: CompressedScalar, b: CompressedScalar, endian: builtin.Endian) NonCanonicalError!CompressedScalar { + return (try Scalar.fromBytes(a, endian)).mul(try Scalar.fromBytes(b, endian)).toBytes(endian); +} + +/// Return a*b+c (mod L) +pub fn mulAdd(a: CompressedScalar, b: CompressedScalar, c: CompressedScalar, endian: builtin.Endian) NonCanonicalError!CompressedScalar { + return (try Scalar.fromBytes(a, endian)).mul(try Scalar.fromBytes(b, endian)).add(try Scalar.fromBytes(c, endian)).toBytes(endian); +} + +/// Return a+b (mod L) +pub fn add(a: CompressedScalar, b: CompressedScalar, endian: builtin.Endian) NonCanonicalError!CompressedScalar { + return (try Scalar.fromBytes(a, endian)).add(try Scalar.fromBytes(b, endian)).toBytes(endian); +} + +/// Return -s (mod L) +pub fn neg(s: CompressedScalar, endian: builtin.Endian) NonCanonicalError!CompressedScalar { + return (try Scalar.fromBytes(a, endian)).neg().toBytes(endian); +} + +/// Return (a-b) (mod L) +pub fn sub(a: CompressedScalar, b: CompressedScalar, endian: builtin.Endian) NonCanonicalError!CompressedScalar { + return (try Scalar.fromBytes(a, endian)).sub(try Scalar.fromBytes(b.endian)).toBytes(endian); +} + +/// Return a random scalar +pub fn random(endian: builtin.Endian) CompressedScalar { + return Scalar.random().toBytes(endian); +} + +/// A scalar in unpacked representation. +pub const Scalar = struct { + fe: Fe, + + /// Zero. + pub const zero = Scalar{ .fe = Fe.zero }; + + /// One. + pub const one = Scalar{ .fe = Fe.one }; + + /// Unpack a serialized representation of a scalar. + pub fn fromBytes(s: CompressedScalar, endian: builtin.Endian) NonCanonicalError!Scalar { + return Scalar{ .fe = try Fe.fromBytes(s, endian) }; + } + + /// Reduce a 384 bit input to the field size. + pub fn fromBytes48(s: [48]u8, endian: builtin.Endian) Scalar { + const t = ScalarDouble.fromBytes(384, s, endian); + return t.reduce(384); + } + + /// Reduce a 512 bit input to the field size. + pub fn fromBytes64(s: [64]u8, endian: builtin.Endian) Scalar { + const t = ScalarDouble.fromBytes(512, s, endian); + return t.reduce(512); + } + + /// Pack a scalar into bytes. + pub fn toBytes(n: Scalar, endian: builtin.Endian) CompressedScalar { + return n.fe.toBytes(endian); + } + + /// Return true if the scalar is zero.. + pub fn isZero(n: Scalar) bool { + return n.fe.isZero(); + } + + /// Return true if a and b are equivalent. + pub fn equivalent(a: Scalar, b: Scalar) bool { + return a.fe.equivalent(b.fe); + } + + /// Compute x+y (mod L) + pub fn add(x: Scalar, y: Scalar) Scalar { + return Scalar{ .fe = x.fe().add(y.fe) }; + } + + /// Compute x-y (mod L) + pub fn sub(x: Scalar, y: Scalar) Scalar { + return Scalar{ .fe = x.fe().sub(y.fe) }; + } + + /// Compute 2n (mod L) + pub fn dbl(n: Scalar) Scalar { + return Scalar{ .fe = n.fe.dbl() }; + } + + /// Compute x*y (mod L) + pub fn mul(x: Scalar, y: Scalar) Scalar { + return Scalar{ .fe = x.fe().mul(y.fe) }; + } + + /// Compute x^2 (mod L) + pub fn sq(n: Scalar) Scalar { + return Scalar{ .fe = n.fe.sq() }; + } + + /// Compute x^n (mod L) + pub fn pow(a: Scalar, comptime T: type, comptime n: T) Scalar { + return Scalar{ .fe = a.fe.pow(n) }; + } + + /// Compute -x (mod L) + pub fn neg(n: Scalar) Scalar { + return Scalar{ .fe = n.fe.neg() }; + } + + /// Compute x^-1 (mod L) + pub fn invert(n: Scalar) Scalar { + return Scalar{ .fe = n.fe.invert() }; + } + + /// Return true if n is a quadratic residue mod L. + pub fn isSquare(n: Scalar) Scalar { + return n.fe.isSquare(); + } + + /// Return the square root of L, or NotSquare if there isn't any solutions. + pub fn sqrt(n: Scalar) NotSquareError!Scalar { + return Scalar{ .fe = try n.fe.sqrt() }; + } + + /// Return a random scalar < L. + pub fn random() Scalar { + var s: [48]u8 = undefined; + while (true) { + crypto.random.bytes(&s); + const n = Scalar.fromBytes48(s, .Little); + if (!n.isZero()) { + return n; + } + } + } +}; + +const ScalarDouble = struct { + x1: Fe, + x2: Fe, + x3: Fe, + + fn fromBytes(comptime bits: usize, s_: [bits / 8]u8, endian: builtin.Endian) ScalarDouble { + debug.assert(bits > 0 and bits <= 512 and bits >= Fe.saturated_bits and bits <= Fe.saturated_bits * 3); + + var s = s_; + if (endian == .Big) { + for (s_) |x, i| s[s.len - 1 - i] = x; + } + var t = ScalarDouble{ .x1 = undefined, .x2 = Fe.zero, .x3 = Fe.zero }; + { + var b = [_]u8{0} ** encoded_length; + const len = math.min(s.len, 24); + mem.copy(u8, b[0..len], s[0..len]); + t.x1 = Fe.fromBytes(b, .Little) catch unreachable; + } + if (s_.len >= 24) { + var b = [_]u8{0} ** encoded_length; + const len = math.min(s.len - 24, 24); + mem.copy(u8, b[0..len], s[24..][0..len]); + t.x2 = Fe.fromBytes(b, .Little) catch unreachable; + } + if (s_.len >= 48) { + var b = [_]u8{0} ** encoded_length; + const len = s.len - 48; + mem.copy(u8, b[0..len], s[48..][0..len]); + t.x3 = Fe.fromBytes(b, .Little) catch unreachable; + } + return t; + } + + fn reduce(expanded: ScalarDouble, comptime bits: usize) Scalar { + debug.assert(bits > 0 and bits <= Fe.saturated_bits * 3 and bits <= 512); + var fe = expanded.x1; + if (bits >= 192) { + const st1 = Fe.fromInt(1 << 192) catch unreachable; + fe = fe.add(expanded.x2.mul(st1)); + if (bits >= 384) { + const st2 = st1.sq(); + fe = fe.add(expanded.x3.mul(st2)); + } + } + return Scalar{ .fe = fe }; + } +}; diff --git a/zig/lib/std/crypto/pcurves/tests.zig b/zig/lib/std/crypto/pcurves/tests.zig new file mode 100644 index 0000000000..4e8e08a7fe --- /dev/null +++ b/zig/lib/std/crypto/pcurves/tests.zig @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2021 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. + +const std = @import("std"); +const fmt = std.fmt; +const testing = std.testing; + +const P256 = @import("p256.zig").P256; + +test "p256 ECDH key exchange" { + const dha = P256.scalar.random(.Little); + const dhb = P256.scalar.random(.Little); + const dhA = try P256.basePoint.mul(dha, .Little); + const dhB = try P256.basePoint.mul(dhb, .Little); + const shareda = try dhA.mul(dhb, .Little); + const sharedb = try dhB.mul(dha, .Little); + testing.expect(shareda.equivalent(sharedb)); +} + +test "p256 point from affine coordinates" { + const xh = "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"; + const yh = "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"; + var xs: [32]u8 = undefined; + _ = try fmt.hexToBytes(&xs, xh); + var ys: [32]u8 = undefined; + _ = try fmt.hexToBytes(&ys, yh); + var p = try P256.fromSerializedAffineCoordinates(xs, ys, .Big); + testing.expect(p.equivalent(P256.basePoint)); +} + +test "p256 test vectors" { + const expected = [_][]const u8{ + "0000000000000000000000000000000000000000000000000000000000000000", + "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", + "7cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978", + "5ecbe4d1a6330a44c8f7ef951d4bf165e6c6b721efada985fb41661bc6e7fd6c", + "e2534a3532d08fbba02dde659ee62bd0031fe2db785596ef509302446b030852", + "51590b7a515140d2d784c85608668fdfef8c82fd1f5be52421554a0dc3d033ed", + "b01a172a76a4602c92d3242cb897dde3024c740debb215b4c6b0aae93c2291a9", + "8e533b6fa0bf7b4625bb30667c01fb607ef9f8b8a80fef5b300628703187b2a3", + "62d9779dbee9b0534042742d3ab54cadc1d238980fce97dbb4dd9dc1db6fb393", + "ea68d7b6fedf0b71878938d51d71f8729e0acb8c2c6df8b3d79e8a4b90949ee0", + }; + var p = P256.identityElement; + for (expected) |xh| { + const x = p.affineCoordinates().x; + p = p.add(P256.basePoint); + var xs: [32]u8 = undefined; + _ = try fmt.hexToBytes(&xs, xh); + testing.expectEqualSlices(u8, &x.toBytes(.Big), &xs); + } +} + +test "p256 test vectors - doubling" { + const expected = [_][]const u8{ + "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", + "7cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978", + "e2534a3532d08fbba02dde659ee62bd0031fe2db785596ef509302446b030852", + "62d9779dbee9b0534042742d3ab54cadc1d238980fce97dbb4dd9dc1db6fb393", + }; + var p = P256.basePoint; + for (expected) |xh| { + const x = p.affineCoordinates().x; + p = p.dbl(); + var xs: [32]u8 = undefined; + _ = try fmt.hexToBytes(&xs, xh); + testing.expectEqualSlices(u8, &x.toBytes(.Big), &xs); + } +} + +test "p256 compressed sec1 encoding/decoding" { + const p = P256.random(); + const s = p.toCompressedSec1(); + const q = try P256.fromSec1(&s); + testing.expect(p.equivalent(q)); +} + +test "p256 uncompressed sec1 encoding/decoding" { + const p = P256.random(); + const s = p.toUncompressedSec1(); + const q = try P256.fromSec1(&s); + testing.expect(p.equivalent(q)); +} + +test "p256 public key is the neutral element" { + const n = P256.scalar.Scalar.zero.toBytes(.Little); + const p = P256.random(); + testing.expectError(error.IdentityElement, p.mul(n, .Little)); +} + +test "p256 public key is the neutral element (public verification)" { + const n = P256.scalar.Scalar.zero.toBytes(.Little); + const p = P256.random(); + testing.expectError(error.IdentityElement, p.mulPublic(n, .Little)); +} + +test "p256 field element non-canonical encoding" { + const s = [_]u8{0xff} ** 32; + testing.expectError(error.NonCanonical, P256.Fe.fromBytes(s, .Little)); +} diff --git a/zig/lib/std/crypto/poly1305.zig b/zig/lib/std/crypto/poly1305.zig index 739c057178..375cf0a3cb 100644 --- a/zig/lib/std/crypto/poly1305.zig +++ b/zig/lib/std/crypto/poly1305.zig @@ -39,7 +39,7 @@ pub const Poly1305 = struct { }; } - fn blocks(st: *Poly1305, m: []const u8, last: comptime bool) void { + fn blocks(st: *Poly1305, m: []const u8, comptime last: bool) void { const hibit: u64 = if (last) 0 else 1 << 40; const r0 = st.r[0]; const r1 = st.r[1]; diff --git a/zig/lib/std/crypto/salsa20.zig b/zig/lib/std/crypto/salsa20.zig index 006767c93f..2a06944adc 100644 --- a/zig/lib/std/crypto/salsa20.zig +++ b/zig/lib/std/crypto/salsa20.zig @@ -15,7 +15,10 @@ const Vector = std.meta.Vector; const Poly1305 = crypto.onetimeauth.Poly1305; const Blake2b = crypto.hash.blake2.Blake2b; const X25519 = crypto.dh.X25519; -const Error = crypto.Error; + +const AuthenticationError = crypto.errors.AuthenticationError; +const IdentityElementError = crypto.errors.IdentityElementError; +const WeakPublicKeyError = crypto.errors.WeakPublicKeyError; const Salsa20VecImpl = struct { const Lane = Vector(4, u32); @@ -399,7 +402,7 @@ pub const XSalsa20Poly1305 = struct { /// ad: Associated Data /// npub: public nonce /// k: private key - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void { debug.assert(c.len == m.len); const extended = extend(k, npub); var block0 = [_]u8{0} ** 64; @@ -447,7 +450,7 @@ pub const SecretBox = struct { /// Verify and decrypt `c` using a nonce `npub` and a key `k`. /// `m` must be exactly `tag_length` smaller than `c`, as `c` includes an authentication tag in addition to the encrypted message. - pub fn open(m: []u8, c: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void { + pub fn open(m: []u8, c: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void { if (c.len < tag_length) { return error.AuthenticationFailed; } @@ -482,20 +485,20 @@ pub const Box = struct { pub const KeyPair = X25519.KeyPair; /// Compute a secret suitable for `secretbox` given a recipent's public key and a sender's secret key. - pub fn createSharedSecret(public_key: [public_length]u8, secret_key: [secret_length]u8) Error![shared_length]u8 { + pub fn createSharedSecret(public_key: [public_length]u8, secret_key: [secret_length]u8) (IdentityElementError || WeakPublicKeyError)![shared_length]u8 { const p = try X25519.scalarmult(secret_key, public_key); const zero = [_]u8{0} ** 16; return Salsa20Impl.hsalsa20(zero, p); } /// Encrypt and authenticate a message using a recipient's public key `public_key` and a sender's `secret_key`. - pub fn seal(c: []u8, m: []const u8, npub: [nonce_length]u8, public_key: [public_length]u8, secret_key: [secret_length]u8) Error!void { + pub fn seal(c: []u8, m: []const u8, npub: [nonce_length]u8, public_key: [public_length]u8, secret_key: [secret_length]u8) (IdentityElementError || WeakPublicKeyError)!void { const shared_key = try createSharedSecret(public_key, secret_key); return SecretBox.seal(c, m, npub, shared_key); } /// Verify and decrypt a message using a recipient's secret key `public_key` and a sender's `public_key`. - pub fn open(m: []u8, c: []const u8, npub: [nonce_length]u8, public_key: [public_length]u8, secret_key: [secret_length]u8) Error!void { + pub fn open(m: []u8, c: []const u8, npub: [nonce_length]u8, public_key: [public_length]u8, secret_key: [secret_length]u8) (IdentityElementError || WeakPublicKeyError || AuthenticationError)!void { const shared_key = try createSharedSecret(public_key, secret_key); return SecretBox.open(m, c, npub, shared_key); } @@ -528,7 +531,7 @@ pub const SealedBox = struct { /// Encrypt a message `m` for a recipient whose public key is `public_key`. /// `c` must be `seal_length` bytes larger than `m`, so that the required metadata can be added. - pub fn seal(c: []u8, m: []const u8, public_key: [public_length]u8) Error!void { + pub fn seal(c: []u8, m: []const u8, public_key: [public_length]u8) (WeakPublicKeyError || IdentityElementError)!void { debug.assert(c.len == m.len + seal_length); var ekp = try KeyPair.create(null); const nonce = createNonce(ekp.public_key, public_key); @@ -539,7 +542,7 @@ pub const SealedBox = struct { /// Decrypt a message using a key pair. /// `m` must be exactly `seal_length` bytes smaller than `c`, as `c` also includes metadata. - pub fn open(m: []u8, c: []const u8, keypair: KeyPair) Error!void { + pub fn open(m: []u8, c: []const u8, keypair: KeyPair) (IdentityElementError || WeakPublicKeyError || AuthenticationError)!void { if (c.len < seal_length) { return error.AuthenticationFailed; } diff --git a/zig/lib/std/crypto/utils.zig b/zig/lib/std/crypto/utils.zig index 08271ac9f4..ca86601bf9 100644 --- a/zig/lib/std/crypto/utils.zig +++ b/zig/lib/std/crypto/utils.zig @@ -1,7 +1,11 @@ const std = @import("../std.zig"); +const debug = std.debug; const mem = std.mem; const testing = std.testing; +const Endian = std.builtin.Endian; +const Order = std.math.Order; + /// Compares two arrays in constant time (for a given length) and returns whether they are equal. /// This function was designed to compare short cryptographic secrets (MACs, signatures). /// For all other applications, use mem.eql() instead. @@ -38,6 +42,41 @@ pub fn timingSafeEql(comptime T: type, a: T, b: T) bool { } } +/// Compare two integers serialized as arrays of the same size, in constant time. +/// Returns .lt if ab and .eq if a=b +pub fn timingSafeCompare(comptime T: type, a: []const T, b: []const T, endian: Endian) Order { + debug.assert(a.len == b.len); + const bits = switch (@typeInfo(T)) { + .Int => |cinfo| if (cinfo.signedness != .unsigned) @compileError("Elements to be compared must be unsigned") else cinfo.bits, + else => @compileError("Elements to be compared must be integers"), + }; + comptime const Cext = std.meta.Int(.unsigned, bits + 1); + var gt: T = 0; + var eq: T = 1; + if (endian == .Little) { + var i = a.len; + while (i != 0) { + i -= 1; + const x1 = a[i]; + const x2 = b[i]; + gt |= @truncate(T, (@as(Cext, x2) -% @as(Cext, x1)) >> bits) & eq; + eq &= @truncate(T, (@as(Cext, (x2 ^ x1)) -% 1) >> bits); + } + } else { + for (a) |x1, i| { + const x2 = b[i]; + gt |= @truncate(T, (@as(Cext, x2) -% @as(Cext, x1)) >> bits) & eq; + eq &= @truncate(T, (@as(Cext, (x2 ^ x1)) -% 1) >> bits); + } + } + if (gt != 0) { + return Order.gt; + } else if (eq != 0) { + return Order.eq; + } + return Order.lt; +} + /// Sets a slice to zeroes. /// Prevents the store from being optimized out. pub fn secureZero(comptime T: type, s: []T) void { @@ -70,6 +109,19 @@ test "crypto.utils.timingSafeEql (vectors)" { testing.expect(timingSafeEql(std.meta.Vector(100, u8), v1, v3)); } +test "crypto.utils.timingSafeCompare" { + var a = [_]u8{10} ** 32; + var b = [_]u8{10} ** 32; + testing.expectEqual(timingSafeCompare(u8, &a, &b, .Big), .eq); + testing.expectEqual(timingSafeCompare(u8, &a, &b, .Little), .eq); + a[31] = 1; + testing.expectEqual(timingSafeCompare(u8, &a, &b, .Big), .lt); + testing.expectEqual(timingSafeCompare(u8, &a, &b, .Little), .lt); + a[0] = 20; + testing.expectEqual(timingSafeCompare(u8, &a, &b, .Big), .gt); + testing.expectEqual(timingSafeCompare(u8, &a, &b, .Little), .lt); +} + test "crypto.utils.secureZero" { var a = [_]u8{0xfe} ** 8; var b = [_]u8{0xfe} ** 8; diff --git a/zig/lib/std/debug.zig b/zig/lib/std/debug.zig index c84a0e0f18..6cca67f302 100644 --- a/zig/lib/std/debug.zig +++ b/zig/lib/std/debug.zig @@ -336,6 +336,13 @@ pub const StackIterator = struct { fp: usize, pub fn init(first_address: ?usize, fp: ?usize) StackIterator { + if (builtin.arch == .sparcv9) { + // Flush all the register windows on stack. + asm volatile ( + \\ flushw + ::: "memory"); + } + return StackIterator{ .first_address = first_address, .fp = fp orelse @frameAddress(), @@ -343,18 +350,18 @@ pub const StackIterator = struct { } // Offset of the saved BP wrt the frame pointer. - const fp_offset = if (builtin.arch.isRISCV()) + const fp_offset = if (comptime builtin.arch.isRISCV()) // On RISC-V the frame pointer points to the top of the saved register // area, on pretty much every other architecture it points to the stack // slot where the previous frame pointer is saved. 2 * @sizeOf(usize) - else if (builtin.arch.isSPARC()) + else if (comptime builtin.arch.isSPARC()) // On SPARC the previous frame pointer is stored at 14 slots past %fp+BIAS. 14 * @sizeOf(usize) else 0; - const fp_bias = if (builtin.arch.isSPARC()) + const fp_bias = if (comptime builtin.arch.isSPARC()) // On SPARC frame pointers are biased by a constant. 2047 else @@ -380,7 +387,7 @@ pub const StackIterator = struct { } fn next_internal(self: *StackIterator) ?usize { - const fp = if (builtin.arch.isSPARC()) + const fp = if (comptime builtin.arch.isSPARC()) // On SPARC the offset is positive. (!) math.add(usize, self.fp, fp_offset) catch return null else diff --git a/zig/lib/std/event/rwlock.zig b/zig/lib/std/event/rwlock.zig index 750131beda..04b45ee308 100644 --- a/zig/lib/std/event/rwlock.zig +++ b/zig/lib/std/event/rwlock.zig @@ -264,7 +264,7 @@ var shared_test_data = [1]i32{0} ** 10; var shared_test_index: usize = 0; var shared_count: usize = 0; fn writeRunner(lock: *RwLock) callconv(.Async) void { - suspend; // resumed by onNextTick + suspend {} // resumed by onNextTick var i: usize = 0; while (i < shared_test_data.len) : (i += 1) { @@ -281,7 +281,7 @@ fn writeRunner(lock: *RwLock) callconv(.Async) void { } } fn readRunner(lock: *RwLock) callconv(.Async) void { - suspend; // resumed by onNextTick + suspend {} // resumed by onNextTick std.time.sleep(1); var i: usize = 0; diff --git a/zig/lib/std/fmt.zig b/zig/lib/std/fmt.zig index bfe28ef203..878969b88a 100644 --- a/zig/lib/std/fmt.zig +++ b/zig/lib/std/fmt.zig @@ -696,6 +696,11 @@ fn formatFloatValue( error.NoSpaceLeft => unreachable, else => |e| return e, }; + } else if (comptime std.mem.eql(u8, fmt, "x")) { + formatFloatHexadecimal(value, options, buf_stream.writer()) catch |err| switch (err) { + error.NoSpaceLeft => unreachable, + else => |e| return e, + }; } else { @compileError("Unsupported format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'"); } @@ -1023,6 +1028,112 @@ pub fn formatFloatScientific( } } +pub fn formatFloatHexadecimal( + value: anytype, + options: FormatOptions, + writer: anytype, +) !void { + if (math.signbit(value)) { + try writer.writeByte('-'); + } + if (math.isNan(value)) { + return writer.writeAll("nan"); + } + if (math.isInf(value)) { + return writer.writeAll("inf"); + } + + const T = @TypeOf(value); + const TU = std.meta.Int(.unsigned, std.meta.bitCount(T)); + + const mantissa_bits = math.floatMantissaBits(T); + const exponent_bits = math.floatExponentBits(T); + const mantissa_mask = (1 << mantissa_bits) - 1; + const exponent_mask = (1 << exponent_bits) - 1; + const exponent_bias = (1 << (exponent_bits - 1)) - 1; + + const as_bits = @bitCast(TU, value); + var mantissa = as_bits & mantissa_mask; + var exponent: i32 = @truncate(u16, (as_bits >> mantissa_bits) & exponent_mask); + + const is_denormal = exponent == 0 and mantissa != 0; + const is_zero = exponent == 0 and mantissa == 0; + + if (is_zero) { + // Handle this case here to simplify the logic below. + try writer.writeAll("0x0"); + if (options.precision) |precision| { + if (precision > 0) { + try writer.writeAll("."); + try writer.writeByteNTimes('0', precision); + } + } else { + try writer.writeAll(".0"); + } + try writer.writeAll("p0"); + return; + } + + if (is_denormal) { + // Adjust the exponent for printing. + exponent += 1; + } else { + // Add the implicit 1. + mantissa |= 1 << mantissa_bits; + } + + // Fill in zeroes to round the mantissa width to a multiple of 4. + if (T == f16) mantissa <<= 2 else if (T == f32) mantissa <<= 1; + + const mantissa_digits = (mantissa_bits + 3) / 4; + + if (options.precision) |precision| { + // Round if needed. + if (precision < mantissa_digits) { + // We always have at least 4 extra bits. + var extra_bits = (mantissa_digits - precision) * 4; + // The result LSB is the Guard bit, we need two more (Round and + // Sticky) to round the value. + while (extra_bits > 2) { + mantissa = (mantissa >> 1) | (mantissa & 1); + extra_bits -= 1; + } + // Round to nearest, tie to even. + mantissa |= @boolToInt(mantissa & 0b100 != 0); + mantissa += 1; + // Drop the excess bits. + mantissa >>= 2; + // Restore the alignment. + mantissa <<= @intCast(math.Log2Int(TU), (mantissa_digits - precision) * 4); + + const overflow = mantissa & (1 << 1 + mantissa_digits * 4) != 0; + // Prefer a normalized result in case of overflow. + if (overflow) { + mantissa >>= 1; + exponent += 1; + } + } + } + + // +1 for the decimal part. + var buf: [1 + mantissa_digits]u8 = undefined; + const N = formatIntBuf(&buf, mantissa, 16, false, .{ .fill = '0', .width = 1 + mantissa_digits }); + + try writer.writeAll("0x"); + try writer.writeByte(buf[0]); + if (options.precision != @as(usize, 0)) + try writer.writeAll("."); + const trimmed = mem.trimRight(u8, buf[1..], "0"); + try writer.writeAll(trimmed); + // Add trailing zeros if explicitly requested. + if (options.precision) |precision| if (precision > 0) { + if (precision > trimmed.len) + try writer.writeByteNTimes('0', precision - trimmed.len); + }; + try writer.writeAll("p"); + try formatInt(exponent - exponent_bias, 10, false, .{}, writer); +} + /// Print a float of the format x.yyyyy where the number of y is specified by the precision argument. /// By default floats are printed at full precision (no rounding). pub fn formatFloatDecimal( @@ -1506,9 +1617,11 @@ test "parseUnsigned" { } pub const parseFloat = @import("fmt/parse_float.zig").parseFloat; +pub const parseHexFloat = @import("fmt/parse_hex_float.zig").parseHexFloat; -test "parseFloat" { +test { _ = @import("fmt/parse_float.zig"); + _ = @import("fmt/parse_hex_float.zig"); } pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { @@ -1898,6 +2011,54 @@ test "float.special" { try expectFmt("f64: -inf", "f64: {}", .{-math.inf_f64}); } +test "float.hexadecimal.special" { + try expectFmt("f64: nan", "f64: {x}", .{math.nan_f64}); + // negative nan is not defined by IEE 754, + // and ARM thus normalizes it to positive nan + if (builtin.arch != builtin.Arch.arm) { + try expectFmt("f64: -nan", "f64: {x}", .{-math.nan_f64}); + } + try expectFmt("f64: inf", "f64: {x}", .{math.inf_f64}); + try expectFmt("f64: -inf", "f64: {x}", .{-math.inf_f64}); + + try expectFmt("f64: 0x0.0p0", "f64: {x}", .{@as(f64, 0)}); + try expectFmt("f64: -0x0.0p0", "f64: {x}", .{-@as(f64, 0)}); +} + +test "float.hexadecimal" { + try expectFmt("f16: 0x1.554p-2", "f16: {x}", .{@as(f16, 1.0 / 3.0)}); + try expectFmt("f32: 0x1.555556p-2", "f32: {x}", .{@as(f32, 1.0 / 3.0)}); + try expectFmt("f64: 0x1.5555555555555p-2", "f64: {x}", .{@as(f64, 1.0 / 3.0)}); + try expectFmt("f128: 0x1.5555555555555555555555555555p-2", "f128: {x}", .{@as(f128, 1.0 / 3.0)}); + + try expectFmt("f16: 0x1.p-14", "f16: {x}", .{@as(f16, math.f16_min)}); + try expectFmt("f32: 0x1.p-126", "f32: {x}", .{@as(f32, math.f32_min)}); + try expectFmt("f64: 0x1.p-1022", "f64: {x}", .{@as(f64, math.f64_min)}); + try expectFmt("f128: 0x1.p-16382", "f128: {x}", .{@as(f128, math.f128_min)}); + + try expectFmt("f16: 0x0.004p-14", "f16: {x}", .{@as(f16, math.f16_true_min)}); + try expectFmt("f32: 0x0.000002p-126", "f32: {x}", .{@as(f32, math.f32_true_min)}); + try expectFmt("f64: 0x0.0000000000001p-1022", "f64: {x}", .{@as(f64, math.f64_true_min)}); + try expectFmt("f128: 0x0.0000000000000000000000000001p-16382", "f128: {x}", .{@as(f128, math.f128_true_min)}); + + try expectFmt("f16: 0x1.ffcp15", "f16: {x}", .{@as(f16, math.f16_max)}); + try expectFmt("f32: 0x1.fffffep127", "f32: {x}", .{@as(f32, math.f32_max)}); + try expectFmt("f64: 0x1.fffffffffffffp1023", "f64: {x}", .{@as(f64, math.f64_max)}); + try expectFmt("f128: 0x1.ffffffffffffffffffffffffffffp16383", "f128: {x}", .{@as(f128, math.f128_max)}); +} + +test "float.hexadecimal.precision" { + try expectFmt("f16: 0x1.5p-2", "f16: {x:.1}", .{@as(f16, 1.0 / 3.0)}); + try expectFmt("f32: 0x1.555p-2", "f32: {x:.3}", .{@as(f32, 1.0 / 3.0)}); + try expectFmt("f64: 0x1.55555p-2", "f64: {x:.5}", .{@as(f64, 1.0 / 3.0)}); + try expectFmt("f128: 0x1.5555555p-2", "f128: {x:.7}", .{@as(f128, 1.0 / 3.0)}); + + try expectFmt("f16: 0x1.00000p0", "f16: {x:.5}", .{@as(f16, 1.0)}); + try expectFmt("f32: 0x1.00000p0", "f32: {x:.5}", .{@as(f32, 1.0)}); + try expectFmt("f64: 0x1.00000p0", "f64: {x:.5}", .{@as(f64, 1.0)}); + try expectFmt("f128: 0x1.00000p0", "f128: {x:.5}", .{@as(f128, 1.0)}); +} + test "float.decimal" { try expectFmt("f64: 152314000000000000000000000000", "f64: {d}", .{@as(f64, 1.52314e+29)}); try expectFmt("f32: 0", "f32: {d}", .{@as(f32, 0.0)}); diff --git a/zig/lib/std/fmt/parse_hex_float.zig b/zig/lib/std/fmt/parse_hex_float.zig new file mode 100644 index 0000000000..4bff267a82 --- /dev/null +++ b/zig/lib/std/fmt/parse_hex_float.zig @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2021 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software.const std = @import("std"); +// +// The rounding logic is inspired by LLVM's APFloat and Go's atofHex +// implementation. + +const std = @import("std"); +const ascii = std.ascii; +const fmt = std.fmt; +const math = std.math; +const testing = std.testing; + +const assert = std.debug.assert; + +pub fn parseHexFloat(comptime T: type, s: []const u8) !T { + assert(@typeInfo(T) == .Float); + + const IntT = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); + + const mantissa_bits = math.floatMantissaBits(T); + const exponent_bits = math.floatExponentBits(T); + + const sign_shift = mantissa_bits + exponent_bits; + + const exponent_bias = (1 << (exponent_bits - 1)) - 1; + const exponent_min = 1 - exponent_bias; + const exponent_max = exponent_bias; + + if (s.len == 0) + return error.InvalidCharacter; + + if (ascii.eqlIgnoreCase(s, "nan")) { + return math.nan(T); + } else if (ascii.eqlIgnoreCase(s, "inf") or ascii.eqlIgnoreCase(s, "+inf")) { + return math.inf(T); + } else if (ascii.eqlIgnoreCase(s, "-inf")) { + return -math.inf(T); + } + + var negative: bool = false; + var exp_negative: bool = false; + + var mantissa: u128 = 0; + var exponent: i16 = 0; + var frac_scale: i16 = 0; + + const State = enum { + MaybeSign, + Prefix, + LeadingIntegerDigit, + IntegerDigit, + MaybeDot, + LeadingFractionDigit, + FractionDigit, + ExpPrefix, + MaybeExpSign, + ExpDigit, + }; + + var state = State.MaybeSign; + + var i: usize = 0; + while (i < s.len) { + const c = s[i]; + + switch (state) { + .MaybeSign => { + state = .Prefix; + + if (c == '+') { + i += 1; + } else if (c == '-') { + negative = true; + i += 1; + } + }, + .Prefix => { + state = .LeadingIntegerDigit; + + // Match both 0x and 0X. + if (i + 2 > s.len or s[i] != '0' or s[i + 1] | 32 != 'x') + return error.InvalidCharacter; + i += 2; + }, + .LeadingIntegerDigit => { + if (c == '0') { + // Skip leading zeros. + i += 1; + } else if (c == '_') { + return error.InvalidCharacter; + } else { + state = .IntegerDigit; + } + }, + .IntegerDigit => { + if (ascii.isXDigit(c)) { + if (mantissa >= math.maxInt(u128) / 16) + return error.Overflow; + mantissa *%= 16; + mantissa += try fmt.charToDigit(c, 16); + i += 1; + } else if (c == '_') { + i += 1; + } else { + state = .MaybeDot; + } + }, + .MaybeDot => { + if (c == '.') { + state = .LeadingFractionDigit; + i += 1; + } else state = .ExpPrefix; + }, + .LeadingFractionDigit => { + if (c == '_') { + return error.InvalidCharacter; + } else state = .FractionDigit; + }, + .FractionDigit => { + if (ascii.isXDigit(c)) { + if (mantissa < math.maxInt(u128) / 16) { + mantissa *%= 16; + mantissa +%= try fmt.charToDigit(c, 16); + frac_scale += 1; + } else if (c != '0') { + return error.Overflow; + } + i += 1; + } else if (c == '_') { + i += 1; + } else { + state = .ExpPrefix; + } + }, + .ExpPrefix => { + state = .MaybeExpSign; + // Match both p and P. + if (c | 32 != 'p') + return error.InvalidCharacter; + i += 1; + }, + .MaybeExpSign => { + state = .ExpDigit; + + if (c == '+') { + i += 1; + } else if (c == '-') { + exp_negative = true; + i += 1; + } + }, + .ExpDigit => { + if (ascii.isXDigit(c)) { + if (exponent >= math.maxInt(i16) / 10) + return error.Overflow; + exponent *%= 10; + exponent +%= try fmt.charToDigit(c, 10); + i += 1; + } else if (c == '_') { + i += 1; + } else { + return error.InvalidCharacter; + } + }, + } + } + + if (exp_negative) + exponent *= -1; + + // Bring the decimal part to the left side of the decimal dot. + exponent -= frac_scale * 4; + + if (mantissa == 0) { + // Signed zero. + return if (negative) -0.0 else 0.0; + } + + // Divide by 2^mantissa_bits to right-align the mantissa in the fractional + // part. + exponent += mantissa_bits; + + // Keep around two extra bits to correctly round any value that doesn't fit + // the available mantissa bits. The result LSB serves as Guard bit, the + // following one is the Round bit and the last one is the Sticky bit, + // computed by OR-ing all the dropped bits. + + // Normalize by aligning the implicit one bit. + while (mantissa >> (mantissa_bits + 2) == 0) { + mantissa <<= 1; + exponent -= 1; + } + + // Normalize again by dropping the excess precision. + // Note that the discarded bits are folded into the Sticky bit. + while (mantissa >> (mantissa_bits + 2 + 1) != 0) { + mantissa = mantissa >> 1 | (mantissa & 1); + exponent += 1; + } + + // Very small numbers can be possibly represented as denormals, reduce the + // exponent as much as possible. + while (mantissa != 0 and exponent < exponent_min - 2) { + mantissa = mantissa >> 1 | (mantissa & 1); + exponent += 1; + } + + // There are two cases to handle: + // - We've truncated more than 0.5ULP (R=S=1), increase the mantissa. + // - We've truncated exactly 0.5ULP (R=1 S=0), increase the mantissa if the + // result is odd (G=1). + // The two checks can be neatly folded as follows. + mantissa |= @boolToInt(mantissa & 0b100 != 0); + mantissa += 1; + + mantissa >>= 2; + exponent += 2; + + if (mantissa & (1 << (mantissa_bits + 1)) != 0) { + // Renormalize, if the exponent overflows we'll catch that below. + mantissa >>= 1; + exponent += 1; + } + + if (mantissa >> mantissa_bits == 0) { + // This is a denormal number, the biased exponent is zero. + exponent = -exponent_bias; + } + + if (exponent > exponent_max) { + // Overflow, return +inf. + return math.inf(T); + } + + // Remove the implicit bit. + mantissa &= @as(u128, (1 << mantissa_bits) - 1); + + const raw: IntT = + (if (negative) @as(IntT, 1) << sign_shift else 0) | + @as(IntT, @bitCast(u16, exponent + exponent_bias)) << mantissa_bits | + @truncate(IntT, mantissa); + + return @bitCast(T, raw); +} + +test "special" { + testing.expect(math.isNan(try parseHexFloat(f32, "nAn"))); + testing.expect(math.isPositiveInf(try parseHexFloat(f32, "iNf"))); + testing.expect(math.isPositiveInf(try parseHexFloat(f32, "+Inf"))); + testing.expect(math.isNegativeInf(try parseHexFloat(f32, "-iNf"))); +} +test "zero" { + testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "0x0")); + testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "-0x0")); + testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "0x0p42")); + testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "-0x0.00000p42")); + testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "0x0.00000p666")); +} + +test "f16" { + const Case = struct { s: []const u8, v: f16 }; + const cases: []const Case = &[_]Case{ + .{ .s = "0x1p0", .v = 1.0 }, + .{ .s = "-0x1p-1", .v = -0.5 }, + .{ .s = "0x10p+10", .v = 16384.0 }, + .{ .s = "0x10p-10", .v = 0.015625 }, + // Max normalized value. + .{ .s = "0x1.ffcp+15", .v = math.f16_max }, + .{ .s = "-0x1.ffcp+15", .v = -math.f16_max }, + // Min normalized value. + .{ .s = "0x1p-14", .v = math.f16_min }, + .{ .s = "-0x1p-14", .v = -math.f16_min }, + // Min denormal value. + .{ .s = "0x1p-24", .v = math.f16_true_min }, + .{ .s = "-0x1p-24", .v = -math.f16_true_min }, + }; + + for (cases) |case| { + testing.expectEqual(case.v, try parseHexFloat(f16, case.s)); + } +} +test "f32" { + const Case = struct { s: []const u8, v: f32 }; + const cases: []const Case = &[_]Case{ + .{ .s = "0x1p0", .v = 1.0 }, + .{ .s = "-0x1p-1", .v = -0.5 }, + .{ .s = "0x10p+10", .v = 16384.0 }, + .{ .s = "0x10p-10", .v = 0.015625 }, + .{ .s = "0x0.ffffffp128", .v = 0x0.ffffffp128 }, + .{ .s = "0x0.1234570p-125", .v = 0x0.1234570p-125 }, + // Max normalized value. + .{ .s = "0x1.fffffeP+127", .v = math.f32_max }, + .{ .s = "-0x1.fffffeP+127", .v = -math.f32_max }, + // Min normalized value. + .{ .s = "0x1p-126", .v = math.f32_min }, + .{ .s = "-0x1p-126", .v = -math.f32_min }, + // Min denormal value. + .{ .s = "0x1P-149", .v = math.f32_true_min }, + .{ .s = "-0x1P-149", .v = -math.f32_true_min }, + }; + + for (cases) |case| { + testing.expectEqual(case.v, try parseHexFloat(f32, case.s)); + } +} +test "f64" { + const Case = struct { s: []const u8, v: f64 }; + const cases: []const Case = &[_]Case{ + .{ .s = "0x1p0", .v = 1.0 }, + .{ .s = "-0x1p-1", .v = -0.5 }, + .{ .s = "0x10p+10", .v = 16384.0 }, + .{ .s = "0x10p-10", .v = 0.015625 }, + // Max normalized value. + .{ .s = "0x1.fffffffffffffp+1023", .v = math.f64_max }, + .{ .s = "-0x1.fffffffffffffp1023", .v = -math.f64_max }, + // Min normalized value. + .{ .s = "0x1p-1022", .v = math.f64_min }, + .{ .s = "-0x1p-1022", .v = -math.f64_min }, + // Min denormalized value. + .{ .s = "0x1p-1074", .v = math.f64_true_min }, + .{ .s = "-0x1p-1074", .v = -math.f64_true_min }, + }; + + for (cases) |case| { + testing.expectEqual(case.v, try parseHexFloat(f64, case.s)); + } +} +test "f128" { + const Case = struct { s: []const u8, v: f128 }; + const cases: []const Case = &[_]Case{ + .{ .s = "0x1p0", .v = 1.0 }, + .{ .s = "-0x1p-1", .v = -0.5 }, + .{ .s = "0x10p+10", .v = 16384.0 }, + .{ .s = "0x10p-10", .v = 0.015625 }, + // Max normalized value. + .{ .s = "0xf.fffffffffffffffffffffffffff8p+16380", .v = math.f128_max }, + .{ .s = "-0xf.fffffffffffffffffffffffffff8p+16380", .v = -math.f128_max }, + // Min normalized value. + .{ .s = "0x1p-16382", .v = math.f128_min }, + .{ .s = "-0x1p-16382", .v = -math.f128_min }, + // // Min denormalized value. + .{ .s = "0x1p-16494", .v = math.f128_true_min }, + .{ .s = "-0x1p-16494", .v = -math.f128_true_min }, + }; + + for (cases) |case| { + testing.expectEqual(@bitCast(u128, case.v), @bitCast(u128, try parseHexFloat(f128, case.s))); + } +} diff --git a/zig/lib/std/json/test.zig b/zig/lib/std/json/test.zig index b6ff03350c..b0d873c910 100644 --- a/zig/lib/std/json/test.zig +++ b/zig/lib/std/json/test.zig @@ -29,8 +29,7 @@ fn ok(s: []const u8) !void { fn err(s: []const u8) void { testing.expect(!json.validate(s)); - testNonStreaming(s) catch return; - testing.expect(false); + testing.expect(std.meta.isError(testNonStreaming(s))); } fn utf8Error(s: []const u8) void { @@ -48,8 +47,7 @@ fn any(s: []const u8) void { fn anyStreamingErrNonStreaming(s: []const u8) void { _ = json.validate(s); - testNonStreaming(s) catch return; - testing.expect(false); + testing.expect(std.meta.isError(testNonStreaming(s))); } fn roundTrip(s: []const u8) !void { diff --git a/zig/lib/std/macho.zig b/zig/lib/std/macho.zig index f66626bafe..8f60d8fe94 100644 --- a/zig/lib/std/macho.zig +++ b/zig/lib/std/macho.zig @@ -1314,11 +1314,11 @@ pub const BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: u8 = 0x40; pub const BIND_OPCODE_SET_TYPE_IMM: u8 = 0x50; pub const BIND_OPCODE_SET_ADDEND_SLEB: u8 = 0x60; pub const BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: u8 = 0x70; -pub const BIND_OPCODE_ADD_ADDR_ULEB: 0x80; +pub const BIND_OPCODE_ADD_ADDR_ULEB: u8 = 0x80; pub const BIND_OPCODE_DO_BIND: u8 = 0x90; pub const BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: u8 = 0xa0; pub const BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: u8 = 0xb0; -pub const BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: u8 = xc0; +pub const BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: u8 = 0xc0; pub const reloc_type_x86_64 = packed enum(u4) { /// for absolute addresses diff --git a/zig/lib/std/math.zig b/zig/lib/std/math.zig index d71cafe5ef..558f541c00 100644 --- a/zig/lib/std/math.zig +++ b/zig/lib/std/math.zig @@ -1349,15 +1349,6 @@ pub fn boolMask(comptime MaskInt: type, value: bool) callconv(.Inline) MaskInt { return @bitCast(i1, @as(u1, @boolToInt(value))); } - // At comptime, -% is disallowed on unsigned values. - // So we need to jump through some hoops in that case. - // This is a workaround for #7951 - if (@typeInfo(@TypeOf(.{value})).Struct.fields[0].is_comptime) { - // Since it's comptime, we don't need this to generate nice code. - // We can just do a branch here. - return if (value) ~@as(MaskInt, 0) else 0; - } - return -%@intCast(MaskInt, @boolToInt(value)); } diff --git a/zig/lib/std/math/copysign.zig b/zig/lib/std/math/copysign.zig index 2804d10495..7861cda8eb 100644 --- a/zig/lib/std/math/copysign.zig +++ b/zig/lib/std/math/copysign.zig @@ -20,6 +20,7 @@ pub fn copysign(comptime T: type, x: T, y: T) T { f16 => copysign16(x, y), f32 => copysign32(x, y), f64 => copysign64(x, y), + f128 => copysign128(x, y), else => @compileError("copysign not implemented for " ++ @typeName(T)), }; } @@ -51,10 +52,20 @@ fn copysign64(x: f64, y: f64) f64 { return @bitCast(f64, h1 | h2); } +fn copysign128(x: f128, y: f128) f128 { + const ux = @bitCast(u128, x); + const uy = @bitCast(u128, y); + + const h1 = ux & (maxInt(u128) / 2); + const h2 = uy & (@as(u128, 1) << 127); + return @bitCast(f128, h1 | h2); +} + test "math.copysign" { expect(copysign(f16, 1.0, 1.0) == copysign16(1.0, 1.0)); expect(copysign(f32, 1.0, 1.0) == copysign32(1.0, 1.0)); expect(copysign(f64, 1.0, 1.0) == copysign64(1.0, 1.0)); + expect(copysign(f128, 1.0, 1.0) == copysign128(1.0, 1.0)); } test "math.copysign16" { @@ -77,3 +88,10 @@ test "math.copysign64" { expect(copysign64(-5.0, -1.0) == -5.0); expect(copysign64(-5.0, 1.0) == 5.0); } + +test "math.copysign128" { + expect(copysign128(5.0, 1.0) == 5.0); + expect(copysign128(5.0, -1.0) == -5.0); + expect(copysign128(-5.0, -1.0) == -5.0); + expect(copysign128(-5.0, 1.0) == 5.0); +} diff --git a/zig/lib/std/math/isfinite.zig b/zig/lib/std/math/isfinite.zig index 5266b918df..11a352b042 100644 --- a/zig/lib/std/math/isfinite.zig +++ b/zig/lib/std/math/isfinite.zig @@ -24,6 +24,10 @@ pub fn isFinite(x: anytype) bool { const bits = @bitCast(u64, x); return bits & (maxInt(u64) >> 1) < (0x7FF << 52); }, + f128 => { + const bits = @bitCast(u128, x); + return bits & (maxInt(u128) >> 1) < (0x7FFF << 112); + }, else => { @compileError("isFinite not implemented for " ++ @typeName(T)); }, @@ -37,10 +41,24 @@ test "math.isFinite" { expect(isFinite(@as(f32, -0.0))); expect(isFinite(@as(f64, 0.0))); expect(isFinite(@as(f64, -0.0))); + expect(isFinite(@as(f128, 0.0))); + expect(isFinite(@as(f128, -0.0))); + expect(!isFinite(math.inf(f16))); expect(!isFinite(-math.inf(f16))); expect(!isFinite(math.inf(f32))); expect(!isFinite(-math.inf(f32))); expect(!isFinite(math.inf(f64))); expect(!isFinite(-math.inf(f64))); + expect(!isFinite(math.inf(f128))); + expect(!isFinite(-math.inf(f128))); + + expect(!isFinite(math.nan(f16))); + expect(!isFinite(-math.nan(f16))); + expect(!isFinite(math.nan(f32))); + expect(!isFinite(-math.nan(f32))); + expect(!isFinite(math.nan(f64))); + expect(!isFinite(-math.nan(f64))); + expect(!isFinite(math.nan(f128))); + expect(!isFinite(-math.nan(f128))); } diff --git a/zig/lib/std/math/signbit.zig b/zig/lib/std/math/signbit.zig index 9fb245c3c6..08963b3d94 100644 --- a/zig/lib/std/math/signbit.zig +++ b/zig/lib/std/math/signbit.zig @@ -14,6 +14,7 @@ pub fn signbit(x: anytype) bool { f16 => signbit16(x), f32 => signbit32(x), f64 => signbit64(x), + f128 => signbit128(x), else => @compileError("signbit not implemented for " ++ @typeName(T)), }; } @@ -33,10 +34,16 @@ fn signbit64(x: f64) bool { return bits >> 63 != 0; } +fn signbit128(x: f128) bool { + const bits = @bitCast(u128, x); + return bits >> 127 != 0; +} + test "math.signbit" { expect(signbit(@as(f16, 4.0)) == signbit16(4.0)); expect(signbit(@as(f32, 4.0)) == signbit32(4.0)); expect(signbit(@as(f64, 4.0)) == signbit64(4.0)); + expect(signbit(@as(f128, 4.0)) == signbit128(4.0)); } test "math.signbit16" { @@ -53,3 +60,8 @@ test "math.signbit64" { expect(!signbit64(4.0)); expect(signbit64(-3.0)); } + +test "math.signbit128" { + expect(!signbit128(4.0)); + expect(signbit128(-3.0)); +} diff --git a/zig/lib/std/math/sqrt.zig b/zig/lib/std/math/sqrt.zig index 394164ff54..4f32581270 100644 --- a/zig/lib/std/math/sqrt.zig +++ b/zig/lib/std/math/sqrt.zig @@ -39,7 +39,13 @@ pub fn sqrt(x: anytype) Sqrt(@TypeOf(x)) { } } -fn sqrt_int(comptime T: type, value: T) std.meta.Int(.unsigned, @typeInfo(T).Int.bits / 2) { +fn sqrt_int(comptime T: type, value: T) Sqrt(T) { + switch (T) { + u0 => return 0, + u1 => return value, + else => {}, + } + var op = value; var res: T = 0; var one: T = 1 << (@typeInfo(T).Int.bits - 2); @@ -58,11 +64,13 @@ fn sqrt_int(comptime T: type, value: T) std.meta.Int(.unsigned, @typeInfo(T).Int one >>= 2; } - const ResultType = std.meta.Int(.unsigned, @typeInfo(T).Int.bits / 2); + const ResultType = Sqrt(T); return @intCast(ResultType, res); } test "math.sqrt_int" { + expect(sqrt_int(u0, 0) == 0); + expect(sqrt_int(u1, 1) == 1); expect(sqrt_int(u32, 3) == 1); expect(sqrt_int(u32, 4) == 2); expect(sqrt_int(u32, 5) == 2); @@ -74,7 +82,13 @@ test "math.sqrt_int" { /// Returns the return type `sqrt` will return given an operand of type `T`. pub fn Sqrt(comptime T: type) type { return switch (@typeInfo(T)) { - .Int => |int| std.meta.Int(.unsigned, int.bits / 2), + .Int => |int| { + return switch (int.bits) { + 0 => u0, + 1 => u1, + else => std.meta.Int(.unsigned, int.bits / 2), + }; + }, else => T, }; } diff --git a/zig/lib/std/mem.zig b/zig/lib/std/mem.zig index 66505f5d29..274da3b8f1 100644 --- a/zig/lib/std/mem.zig +++ b/zig/lib/std/mem.zig @@ -1876,7 +1876,11 @@ test "rotate" { /// Replace needle with replacement as many times as possible, writing to an output buffer which is assumed to be of /// appropriate size. Use replacementSize to calculate an appropriate buffer size. +/// The needle must not be empty. pub fn replace(comptime T: type, input: []const T, needle: []const T, replacement: []const T, output: []T) usize { + // Empty needle will loop until output buffer overflows. + assert(needle.len > 0); + var i: usize = 0; var slide: usize = 0; var replacements: usize = 0; @@ -1899,22 +1903,48 @@ pub fn replace(comptime T: type, input: []const T, needle: []const T, replacemen test "replace" { var output: [29]u8 = undefined; var replacements = replace(u8, "All your base are belong to us", "base", "Zig", output[0..]); + var expected: []const u8 = "All your Zig are belong to us"; testing.expect(replacements == 1); - testing.expect(eql(u8, output[0..], "All your Zig are belong to us")); + testing.expectEqualStrings(expected, output[0..expected.len]); replacements = replace(u8, "Favor reading code over writing code.", "code", "", output[0..]); + expected = "Favor reading over writing ."; testing.expect(replacements == 2); - testing.expect(eql(u8, output[0..], "Favor reading over writing .")); + testing.expectEqualStrings(expected, output[0..expected.len]); + + // Empty needle is not allowed but input may be empty. + replacements = replace(u8, "", "x", "y", output[0..0]); + expected = ""; + testing.expect(replacements == 0); + testing.expectEqualStrings(expected, output[0..expected.len]); + + // Adjacent replacements. + + replacements = replace(u8, "\\n\\n", "\\n", "\n", output[0..]); + expected = "\n\n"; + testing.expect(replacements == 2); + testing.expectEqualStrings(expected, output[0..expected.len]); + + replacements = replace(u8, "abbba", "b", "cd", output[0..]); + expected = "acdcdcda"; + testing.expect(replacements == 3); + testing.expectEqualStrings(expected, output[0..expected.len]); } /// Calculate the size needed in an output buffer to perform a replacement. +/// The needle must not be empty. pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, replacement: []const T) usize { + // Empty needle will loop forever. + assert(needle.len > 0); + var i: usize = 0; var size: usize = input.len; - while (i < input.len) : (i += 1) { + while (i < input.len) { if (mem.indexOf(T, input[i..], needle) == @as(usize, 0)) { size = size - needle.len + replacement.len; i += needle.len; + } else { + i += 1; } } @@ -1923,9 +1953,15 @@ pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, re test "replacementSize" { testing.expect(replacementSize(u8, "All your base are belong to us", "base", "Zig") == 29); - testing.expect(replacementSize(u8, "", "", "") == 0); testing.expect(replacementSize(u8, "Favor reading code over writing code.", "code", "") == 29); testing.expect(replacementSize(u8, "Only one obvious way to do things.", "things.", "things in Zig.") == 41); + + // Empty needle is not allowed but input may be empty. + testing.expect(replacementSize(u8, "", "x", "y") == 0); + + // Adjacent replacements. + testing.expect(replacementSize(u8, "\\n\\n", "\\n", "\n") == 2); + testing.expect(replacementSize(u8, "abbba", "b", "cd") == 8); } /// Perform a replacement on an allocated buffer of pre-determined size. Caller must free returned memory. diff --git a/zig/lib/std/meta.zig b/zig/lib/std/meta.zig index 860b6874c0..600f3d3c5d 100644 --- a/zig/lib/std/meta.zig +++ b/zig/lib/std/meta.zig @@ -117,10 +117,21 @@ test "std.meta.bitCount" { testing.expect(bitCount(f32) == 32); } +/// Returns the alignment of type T. +/// Note that if T is a pointer or function type the result is different than +/// the one returned by @alignOf(T). +/// If T is a pointer type the alignment of the type it points to is returned. +/// If T is a function type the alignment a target-dependent value is returned. pub fn alignment(comptime T: type) comptime_int { - //@alignOf works on non-pointer types - const P = if (comptime trait.is(.Pointer)(T)) T else *T; - return @typeInfo(P).Pointer.alignment; + return switch (@typeInfo(T)) { + .Optional => |info| switch (@typeInfo(info.child)) { + .Pointer, .Fn => alignment(info.child), + else => @alignOf(T), + }, + .Pointer => |info| info.alignment, + .Fn => |info| info.alignment, + else => @alignOf(T), + }; } test "std.meta.alignment" { @@ -129,6 +140,8 @@ test "std.meta.alignment" { testing.expect(alignment(*align(2) u8) == 2); testing.expect(alignment([]align(1) u8) == 1); testing.expect(alignment([]align(2) u8) == 2); + testing.expect(alignment(fn () void) > 0); + testing.expect(alignment(fn () align(128) void) == 128); } pub fn Child(comptime T: type) type { @@ -888,7 +901,7 @@ pub fn Vector(comptime len: u32, comptime child: type) type { /// Given a type and value, cast the value to the type as c would. /// This is for translate-c and is not intended for general use. pub fn cast(comptime DestType: type, target: anytype) DestType { - // this function should behave like transCCast in translate-c, except it's for macros + // this function should behave like transCCast in translate-c, except it's for macros and enums const SourceType = @TypeOf(target); switch (@typeInfo(DestType)) { .Pointer => { @@ -925,9 +938,10 @@ pub fn cast(comptime DestType: type, target: anytype) DestType { } } }, - .Enum => { + .Enum => |enum_type| { if (@typeInfo(SourceType) == .Int or @typeInfo(SourceType) == .ComptimeInt) { - return @intToEnum(DestType, target); + const intermediate = cast(enum_type.tag_type, target); + return @intToEnum(DestType, intermediate); } }, .Int => { @@ -1015,6 +1029,17 @@ test "std.meta.cast" { testing.expectEqual(@intToPtr(*u8, 2), cast(*u8, @intToPtr(*volatile u8, 2))); testing.expectEqual(@intToPtr(?*c_void, 2), cast(?*c_void, @intToPtr(*u8, 2))); + + const C_ENUM = extern enum(c_int) { + A = 0, + B, + C, + _, + }; + testing.expectEqual(cast(C_ENUM, @as(i64, -1)), @intToEnum(C_ENUM, -1)); + testing.expectEqual(cast(C_ENUM, @as(i8, 1)), .B); + testing.expectEqual(cast(C_ENUM, @as(u64, 1)), .B); + testing.expectEqual(cast(C_ENUM, @as(u64, 42)), @intToEnum(C_ENUM, 42)); } /// Given a value returns its size as C's sizeof operator would. @@ -1334,3 +1359,13 @@ test "shuffleVectorIndex" { testing.expect(shuffleVectorIndex(6, vector_len) == -3); testing.expect(shuffleVectorIndex(7, vector_len) == -4); } + +/// Returns whether `error_union` contains an error. +pub fn isError(error_union: anytype) bool { + return if (error_union) |_| false else |_| true; +} + +test "isError" { + std.testing.expect(isError(math.absInt(@as(i8, -128)))); + std.testing.expect(!isError(math.absInt(@as(i8, -127)))); +} diff --git a/zig/lib/std/os.zig b/zig/lib/std/os.zig index 9826ba46f1..ef6d6a85cd 100644 --- a/zig/lib/std/os.zig +++ b/zig/lib/std/os.zig @@ -675,6 +675,9 @@ pub const WriteError = error{ /// This error occurs when no global event loop is configured, /// and reading from the file descriptor would block. WouldBlock, + + /// Connection reset by peer. + ConnectionResetByPeer, } || UnexpectedError; /// Write to a file descriptor. @@ -752,6 +755,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { ENOSPC => return error.NoSpaceLeft, EPERM => return error.AccessDenied, EPIPE => return error.BrokenPipe, + ECONNRESET => return error.ConnectionResetByPeer, else => |err| return unexpectedErrno(err), } } @@ -820,6 +824,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { ENOSPC => return error.NoSpaceLeft, EPERM => return error.AccessDenied, EPIPE => return error.BrokenPipe, + ECONNRESET => return error.ConnectionResetByPeer, else => |err| return unexpectedErrno(err), } } @@ -1044,7 +1049,7 @@ pub const OpenError = error{ } || UnexpectedError; /// Open and possibly create a file. Keeps trying if it gets interrupted. -/// See also `openC`. +/// See also `openZ`. pub fn open(file_path: []const u8, flags: u32, perm: mode_t) OpenError!fd_t { if (std.Target.current.os.tag == .windows) { const file_path_w = try windows.sliceToPrefixedFileW(file_path); @@ -1142,7 +1147,7 @@ pub fn openW(file_path_w: []const u16, flags: u32, perm: mode_t) OpenError!fd_t /// Open and possibly create a file. Keeps trying if it gets interrupted. /// `file_path` is relative to the open directory handle `dir_fd`. -/// See also `openatC`. +/// See also `openatZ`. pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) OpenError!fd_t { if (builtin.os.tag == .wasi) { @compileError("use openatWasi instead"); @@ -1749,7 +1754,7 @@ pub const UnlinkError = error{ } || UnexpectedError; /// Delete a name and possibly the file it refers to. -/// See also `unlinkC`. +/// See also `unlinkZ`. pub fn unlink(file_path: []const u8) UnlinkError!void { if (builtin.os.tag == .wasi) { @compileError("unlink is not supported in WASI; use unlinkat instead"); @@ -3414,7 +3419,7 @@ pub fn fstat(fd: fd_t) FStatError!Stat { } } -pub const FStatAtError = FStatError || error{ NameTooLong, FileNotFound }; +pub const FStatAtError = FStatError || error{ NameTooLong, FileNotFound, SymLinkLoop }; /// Similar to `fstat`, but returns stat of a resource pointed to by `pathname` /// which is relative to `dirfd` handle. @@ -3461,8 +3466,10 @@ pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!S EBADF => unreachable, // Always a race condition. ENOMEM => return error.SystemResources, EACCES => return error.AccessDenied, + EPERM => return error.AccessDenied, EFAULT => unreachable, ENAMETOOLONG => return error.NameTooLong, + ELOOP => return error.SymLinkLoop, ENOENT => return error.FileNotFound, ENOTDIR => return error.FileNotFound, else => |err| return unexpectedErrno(err), diff --git a/zig/lib/std/os/bits/darwin.zig b/zig/lib/std/os/bits/darwin.zig index aca24b1c0c..e8a477c5d6 100644 --- a/zig/lib/std/os/bits/darwin.zig +++ b/zig/lib/std/os/bits/darwin.zig @@ -832,6 +832,13 @@ pub const SO_RCVTIMEO = 0x1006; pub const SO_ERROR = 0x1007; pub const SO_TYPE = 0x1008; +pub const SO_NREAD = 0x1020; +pub const SO_NKE = 0x1021; +pub const SO_NOSIGPIPE = 0x1022; +pub const SO_NOADDRERR = 0x1023; +pub const SO_NWRITE = 0x1024; +pub const SO_REUSESHAREUID = 0x1025; + fn wstatus(x: u32) u32 { return x & 0o177; } diff --git a/zig/lib/std/os/bits/haiku.zig b/zig/lib/std/os/bits/haiku.zig index 32093570d7..25a881ba56 100644 --- a/zig/lib/std/os/bits/haiku.zig +++ b/zig/lib/std/os/bits/haiku.zig @@ -279,20 +279,10 @@ pub const PROT_READ = 1; pub const PROT_WRITE = 2; pub const PROT_EXEC = 4; -pub const CLOCK_REALTIME = 0; -pub const CLOCK_VIRTUAL = 1; -pub const CLOCK_PROF = 2; -pub const CLOCK_MONOTONIC = 4; -pub const CLOCK_UPTIME = 5; -pub const CLOCK_UPTIME_PRECISE = 7; -pub const CLOCK_UPTIME_FAST = 8; -pub const CLOCK_REALTIME_PRECISE = 9; -pub const CLOCK_REALTIME_FAST = 10; -pub const CLOCK_MONOTONIC_PRECISE = 11; -pub const CLOCK_MONOTONIC_FAST = 12; -pub const CLOCK_SECOND = 13; -pub const CLOCK_THREAD_CPUTIME_ID = 14; -pub const CLOCK_PROCESS_CPUTIME_ID = 15; +pub const CLOCK_MONOTONIC = 0; +pub const CLOCK_REALTIME = -1; +pub const CLOCK_PROCESS_CPUTIME_ID = -2; +pub const CLOCK_THREAD_CPUTIME_ID = -3; pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize)); pub const MAP_SHARED = 0x0001; @@ -310,58 +300,59 @@ pub const MAP_NOCORE = 0x00020000; pub const MAP_PREFAULT_READ = 0x00040000; pub const MAP_32BIT = 0x00080000; -pub const WNOHANG = 1; -pub const WUNTRACED = 2; -pub const WSTOPPED = WUNTRACED; -pub const WCONTINUED = 4; -pub const WNOWAIT = 8; -pub const WEXITED = 16; -pub const WTRAPPED = 32; - -pub const SA_ONSTACK = 0x0001; -pub const SA_RESTART = 0x0002; -pub const SA_RESETHAND = 0x0004; -pub const SA_NOCLDSTOP = 0x0008; -pub const SA_NODEFER = 0x0010; -pub const SA_NOCLDWAIT = 0x0020; -pub const SA_SIGINFO = 0x0040; +pub const WNOHANG = 0x1; +pub const WUNTRACED = 0x2; +pub const WSTOPPED = 0x10; +pub const WCONTINUED = 0x4; +pub const WNOWAIT = 0x20; +pub const WEXITED = 0x08; + +pub const SA_ONSTACK = 0x20; +pub const SA_RESTART = 0x10; +pub const SA_RESETHAND = 0x04; +pub const SA_NOCLDSTOP = 0x01; +pub const SA_NODEFER = 0x08; +pub const SA_NOCLDWAIT = 0x02; +pub const SA_SIGINFO = 0x40; +pub const SA_NOMASK = SA_NODEFER; +pub const SA_STACK = SA_ONSTACK; +pub const SA_ONESHOT = SA_RESETHAND; pub const SIGHUP = 1; pub const SIGINT = 2; pub const SIGQUIT = 3; pub const SIGILL = 4; -pub const SIGTRAP = 5; +pub const SIGCHLD = 5; pub const SIGABRT = 6; pub const SIGIOT = SIGABRT; -pub const SIGEMT = 7; +pub const SIGPIPE = 7; pub const SIGFPE = 8; pub const SIGKILL = 9; -pub const SIGBUS = 10; +pub const SIGSTOP = 10; pub const SIGSEGV = 11; -pub const SIGSYS = 12; -pub const SIGPIPE = 13; +pub const SIGCONT = 12; +pub const SIGTSTP = 13; pub const SIGALRM = 14; pub const SIGTERM = 15; -pub const SIGURG = 16; -pub const SIGSTOP = 17; -pub const SIGTSTP = 18; -pub const SIGCONT = 19; -pub const SIGCHLD = 20; -pub const SIGTTIN = 21; -pub const SIGTTOU = 22; -pub const SIGIO = 23; -pub const SIGXCPU = 24; -pub const SIGXFSZ = 25; -pub const SIGVTALRM = 26; -pub const SIGPROF = 27; -pub const SIGWINCH = 28; -pub const SIGINFO = 29; -pub const SIGUSR1 = 30; -pub const SIGUSR2 = 31; -pub const SIGTHR = 32; -pub const SIGLWP = SIGTHR; -pub const SIGLIBRT = 33; - +pub const SIGTTIN = 16; +pub const SIGTTOU = 17; +pub const SIGUSR1 = 18; +pub const SIGUSR2 = 19; +pub const SIGWINCH = 20; +pub const SIGKILLTHR = 21; +pub const SIGTRAP = 22; +pub const SIGPOLL = 23; +pub const SIGPROF = 24; +pub const SIGSYS = 25; +pub const SIGURG = 26; +pub const SIGVTALRM = 27; +pub const SIGXCPU = 28; +pub const SIGXFSZ = 29; +pub const SIGBUS = 30; +pub const SIGRESERVED1 = 31; +pub const SIGRESERVED2 = 32; + +// TODO: check pub const SIGRTMIN = 65; pub const SIGRTMAX = 126; @@ -645,135 +636,51 @@ pub const EVFILT_SENDFILE = -12; pub const EVFILT_EMPTY = -13; -/// On input, NOTE_TRIGGER causes the event to be triggered for output. -pub const NOTE_TRIGGER = 0x01000000; - -/// ignore input fflags -pub const NOTE_FFNOP = 0x00000000; - -/// and fflags -pub const NOTE_FFAND = 0x40000000; - -/// or fflags -pub const NOTE_FFOR = 0x80000000; - -/// copy fflags -pub const NOTE_FFCOPY = 0xc0000000; - -/// mask for operations -pub const NOTE_FFCTRLMASK = 0xc0000000; -pub const NOTE_FFLAGSMASK = 0x00ffffff; - -/// low water mark -pub const NOTE_LOWAT = 0x00000001; - -/// behave like poll() -pub const NOTE_FILE_POLL = 0x00000002; - -/// vnode was removed -pub const NOTE_DELETE = 0x00000001; - -/// data contents changed -pub const NOTE_WRITE = 0x00000002; - -/// size increased -pub const NOTE_EXTEND = 0x00000004; - -/// attributes changed -pub const NOTE_ATTRIB = 0x00000008; - -/// link count changed -pub const NOTE_LINK = 0x00000010; - -/// vnode was renamed -pub const NOTE_RENAME = 0x00000020; - -/// vnode access was revoked -pub const NOTE_REVOKE = 0x00000040; - -/// vnode was opened -pub const NOTE_OPEN = 0x00000080; - -/// file closed, fd did not allow write -pub const NOTE_CLOSE = 0x00000100; - -/// file closed, fd did allow write -pub const NOTE_CLOSE_WRITE = 0x00000200; - -/// file was read -pub const NOTE_READ = 0x00000400; - -/// process exited -pub const NOTE_EXIT = 0x80000000; - -/// process forked -pub const NOTE_FORK = 0x40000000; - -/// process exec'd -pub const NOTE_EXEC = 0x20000000; - -/// mask for signal & exit status -pub const NOTE_PDATAMASK = 0x000fffff; -pub const NOTE_PCTRLMASK = (~NOTE_PDATAMASK); - -/// data is seconds -pub const NOTE_SECONDS = 0x00000001; - -/// data is milliseconds -pub const NOTE_MSECONDS = 0x00000002; - -/// data is microseconds -pub const NOTE_USECONDS = 0x00000004; - -/// data is nanoseconds -pub const NOTE_NSECONDS = 0x00000008; - -/// timeout is absolute -pub const NOTE_ABSTIME = 0x00000010; - -pub const TIOCEXCL = 0x2000740d; -pub const TIOCNXCL = 0x2000740e; -pub const TIOCSCTTY = 0x20007461; -pub const TIOCGPGRP = 0x40047477; -pub const TIOCSPGRP = 0x80047476; -pub const TIOCOUTQ = 0x40047473; -pub const TIOCSTI = 0x80017472; -pub const TIOCGWINSZ = 0x40087468; -pub const TIOCSWINSZ = 0x80087467; -pub const TIOCMGET = 0x4004746a; -pub const TIOCMBIS = 0x8004746c; -pub const TIOCMBIC = 0x8004746b; -pub const TIOCMSET = 0x8004746d; -pub const FIONREAD = 0x4004667f; -pub const TIOCCONS = 0x80047462; -pub const TIOCPKT = 0x80047470; -pub const FIONBIO = 0x8004667e; -pub const TIOCNOTTY = 0x20007471; -pub const TIOCSETD = 0x8004741b; -pub const TIOCGETD = 0x4004741a; -pub const TIOCSBRK = 0x2000747b; -pub const TIOCCBRK = 0x2000747a; -pub const TIOCGSID = 0x40047463; -pub const TIOCGPTN = 0x4004740f; -pub const TIOCSIG = 0x2004745f; +pub const TCGETA = 0x8000; +pub const TCSETA = 0x8001; +pub const TCSETAW = 0x8004; +pub const TCSETAF = 0x8003; +pub const TCSBRK = 08005; +pub const TCXONC = 0x8007; +pub const TCFLSH = 0x8006; + +pub const TIOCSCTTY = 0x8017; +pub const TIOCGPGRP = 0x8015; +pub const TIOCSPGRP = 0x8016; +pub const TIOCGWINSZ = 0x8012; +pub const TIOCSWINSZ = 0x8013; +pub const TIOCMGET = 0x8018; +pub const TIOCMBIS = 0x8022; +pub const TIOCMBIC = 0x8023; +pub const TIOCMSET = 0x8019; +pub const FIONREAD = 0xbe000001; +pub const FIONBIO = 0xbe000000; +pub const TIOCSBRK = 0x8020; +pub const TIOCCBRK = 0x8021; +pub const TIOCGSID = 0x8024; pub fn WEXITSTATUS(s: u32) u32 { - return (s & 0xff00) >> 8; + return (s & 0xff); } + pub fn WTERMSIG(s: u32) u32 { - return s & 0x7f; + return (s >> 8) & 0xff; } + pub fn WSTOPSIG(s: u32) u32 { return WEXITSTATUS(s); } + pub fn WIFEXITED(s: u32) bool { return WTERMSIG(s) == 0; } + pub fn WIFSTOPPED(s: u32) bool { - return @intCast(u16, (((s & 0xffff) *% 0x10001) >> 8)) > 0x7f00; + return ((s >> 16) & 0xff) != 0; } + pub fn WIFSIGNALED(s: u32) bool { - return (s & 0xffff) -% 1 < 0xff; + return ((s >> 8) & 0xff) != 0; } pub const winsize = extern struct { @@ -823,49 +730,47 @@ pub const sigset_t = extern struct { __bits: [_SIG_WORDS]u32, }; -pub const EPERM = 1; // Operation not permitted -pub const ENOENT = 2; // No such file or directory -pub const ESRCH = 3; // No such process -pub const EINTR = 4; // Interrupted system call -pub const EIO = 5; // Input/output error -pub const ENXIO = 6; // Device not configured -pub const E2BIG = 7; // Argument list too long -pub const ENOEXEC = 8; // Exec format error -pub const EBADF = 9; // Bad file descriptor -pub const ECHILD = 10; // No child processes -pub const EDEADLK = 11; // Resource deadlock avoided -// 11 was EAGAIN -pub const ENOMEM = 12; // Cannot allocate memory -pub const EACCES = 13; // Permission denied -pub const EFAULT = 14; // Bad address -pub const ENOTBLK = 15; // Block device required -pub const EBUSY = 16; // Device busy -pub const EEXIST = 17; // File exists -pub const EXDEV = 18; // Cross-device link -pub const ENODEV = 19; // Operation not supported by device -pub const ENOTDIR = 20; // Not a directory -pub const EISDIR = 21; // Is a directory -pub const EINVAL = 22; // Invalid argument -pub const ENFILE = 23; // Too many open files in system -pub const EMFILE = 24; // Too many open files -pub const ENOTTY = 25; // Inappropriate ioctl for device -pub const ETXTBSY = 26; // Text file busy -pub const EFBIG = 27; // File too large -pub const ENOSPC = 28; // No space left on device -pub const ESPIPE = 29; // Illegal seek -pub const EROFS = 30; // Read-only filesystem -pub const EMLINK = 31; // Too many links -pub const EPIPE = 32; // Broken pipe +pub const EPERM = -0x7ffffff1; // Operation not permitted +pub const ENOENT = -0x7fff9ffd; // No such file or directory +pub const ESRCH = -0x7fff8ff3; // No such process +pub const EINTR = -0x7ffffff6; // Interrupted system call +pub const EIO = -0x7fffffff; // Input/output error +pub const ENXIO = -0x7fff8ff5; // Device not configured +pub const E2BIG = -0x7fff8fff; // Argument list too long +pub const ENOEXEC = -0x7fffecfe; // Exec format error +pub const ECHILD = -0x7fff8ffe; // No child processes +pub const EDEADLK = -0x7fff8ffd; // Resource deadlock avoided +pub const ENOMEM = -0x80000000; // Cannot allocate memory +pub const EACCES = -0x7ffffffe; // Permission denied +pub const EFAULT = -0x7fffecff; // Bad address +pub const EBUSY = -0x7ffffff2; // Device busy +pub const EEXIST = -0x7fff9ffe; // File exists +pub const EXDEV = -0x7fff9ff5; // Cross-device link +pub const ENODEV = -0x7fff8ff9; // Operation not supported by device +pub const ENOTDIR = -0x7fff9ffb; // Not a directory +pub const EISDIR = -0x7fff9ff7; // Is a directory +pub const EINVAL = -0x7ffffffb; // Invalid argument +pub const ENFILE = -0x7fff8ffa; // Too many open files in system +pub const EMFILE = -0x7fff9ff6; // Too many open files +pub const ENOTTY = -0x7fff8ff6; // Inappropriate ioctl for device +pub const ETXTBSY = -0x7fff8fc5; // Text file busy +pub const EFBIG = -0x7fff8ffc; // File too large +pub const ENOSPC = -0x7fff9ff9; // No space left on device +pub const ESPIPE = -0x7fff8ff4; // Illegal seek +pub const EROFS = -0x7fff9ff8; // Read-only filesystem +pub const EMLINK = -0x7fff8ffb; // Too many links +pub const EPIPE = -0x7fff9ff3; // Broken pipe +pub const EBADF = -0x7fffa000; // Bad file descriptor // math software pub const EDOM = 33; // Numerical argument out of domain pub const ERANGE = 34; // Result too large // non-blocking and interrupt i/o -pub const EAGAIN = 35; // Resource temporarily unavailable -pub const EWOULDBLOCK = EAGAIN; // Operation would block -pub const EINPROGRESS = 36; // Operation now in progress -pub const EALREADY = 37; // Operation already in progress +pub const EAGAIN = -0x7ffffff5; +pub const EWOULDBLOCK = -0x7ffffff5; +pub const EINPROGRESS = -0x7fff8fdc; +pub const EALREADY = -0x7fff8fdb; // ipc/network software -- argument errors pub const ENOTSOCK = 38; // Socket operation on non-socket @@ -1447,3 +1352,20 @@ pub const directory_which = extern enum(c_int) { _, }; + +pub const cc_t = u8; +pub const speed_t = u8; +pub const tcflag_t = u32; + +pub const NCCS = 32; + +pub const termios = extern struct { + c_iflag: tcflag_t, + c_oflag: tcflag_t, + c_cflag: tcflag_t, + c_lflag: tcflag_t, + c_line: cc_t, + c_ispeed: speed_t, + c_ospeed: speed_t, + cc_t: [NCCS]cc_t, +}; diff --git a/zig/lib/std/os/bits/linux.zig b/zig/lib/std/os/bits/linux.zig index 86d89016f3..74f9b14ca2 100644 --- a/zig/lib/std/os/bits/linux.zig +++ b/zig/lib/std/os/bits/linux.zig @@ -10,6 +10,7 @@ usingnamespace @import("../bits.zig"); pub usingnamespace switch (builtin.arch) { .mips, .mipsel => @import("linux/errno-mips.zig"), + .sparc, .sparcel, .sparcv9 => @import("linux/errno-sparc.zig"), else => @import("linux/errno-generic.zig"), }; @@ -31,6 +32,7 @@ pub usingnamespace @import("linux/prctl.zig"); pub usingnamespace @import("linux/securebits.zig"); const is_mips = builtin.arch.isMIPS(); +const is_ppc = builtin.arch.isPPC(); const is_ppc64 = builtin.arch.isPPC64(); const is_sparc = builtin.arch.isSPARC(); @@ -247,40 +249,78 @@ else pub const SIG_SETMASK = 2; }; -pub const SIGHUP = 1; -pub const SIGINT = 2; -pub const SIGQUIT = 3; -pub const SIGILL = 4; -pub const SIGTRAP = 5; -pub const SIGABRT = 6; -pub const SIGIOT = SIGABRT; -pub const SIGBUS = 7; -pub const SIGFPE = 8; -pub const SIGKILL = 9; -pub const SIGUSR1 = 10; -pub const SIGSEGV = 11; -pub const SIGUSR2 = 12; -pub const SIGPIPE = 13; -pub const SIGALRM = 14; -pub const SIGTERM = 15; -pub const SIGSTKFLT = 16; -pub const SIGCHLD = 17; -pub const SIGCONT = 18; -pub const SIGSTOP = 19; -pub const SIGTSTP = 20; -pub const SIGTTIN = 21; -pub const SIGTTOU = 22; -pub const SIGURG = 23; -pub const SIGXCPU = 24; -pub const SIGXFSZ = 25; -pub const SIGVTALRM = 26; -pub const SIGPROF = 27; -pub const SIGWINCH = 28; -pub const SIGIO = 29; -pub const SIGPOLL = 29; -pub const SIGPWR = 30; -pub const SIGSYS = 31; -pub const SIGUNUSED = SIGSYS; +pub usingnamespace if (is_sparc) struct { + pub const SIGHUP = 1; + pub const SIGINT = 2; + pub const SIGQUIT = 3; + pub const SIGILL = 4; + pub const SIGTRAP = 5; + pub const SIGABRT = 6; + pub const SIGEMT = 7; + pub const SIGFPE = 8; + pub const SIGKILL = 9; + pub const SIGBUS = 10; + pub const SIGSEGV = 11; + pub const SIGSYS = 12; + pub const SIGPIPE = 13; + pub const SIGALRM = 14; + pub const SIGTERM = 15; + pub const SIGURG = 16; + pub const SIGSTOP = 17; + pub const SIGTSTP = 18; + pub const SIGCONT = 19; + pub const SIGCHLD = 20; + pub const SIGTTIN = 21; + pub const SIGTTOU = 22; + pub const SIGPOLL = 23; + pub const SIGXCPU = 24; + pub const SIGXFSZ = 25; + pub const SIGVTALRM = 26; + pub const SIGPROF = 27; + pub const SIGWINCH = 28; + pub const SIGLOST = 29; + pub const SIGUSR1 = 30; + pub const SIGUSR2 = 31; + pub const SIGIOT = SIGABRT; + pub const SIGCLD = SIGCHLD; + pub const SIGPWR = SIGLOST; + pub const SIGIO = SIGPOLL; +} else struct { + pub const SIGHUP = 1; + pub const SIGINT = 2; + pub const SIGQUIT = 3; + pub const SIGILL = 4; + pub const SIGTRAP = 5; + pub const SIGABRT = 6; + pub const SIGIOT = SIGABRT; + pub const SIGBUS = 7; + pub const SIGFPE = 8; + pub const SIGKILL = 9; + pub const SIGUSR1 = 10; + pub const SIGSEGV = 11; + pub const SIGUSR2 = 12; + pub const SIGPIPE = 13; + pub const SIGALRM = 14; + pub const SIGTERM = 15; + pub const SIGSTKFLT = 16; + pub const SIGCHLD = 17; + pub const SIGCONT = 18; + pub const SIGSTOP = 19; + pub const SIGTSTP = 20; + pub const SIGTTIN = 21; + pub const SIGTTOU = 22; + pub const SIGURG = 23; + pub const SIGXCPU = 24; + pub const SIGXFSZ = 25; + pub const SIGVTALRM = 26; + pub const SIGPROF = 27; + pub const SIGWINCH = 28; + pub const SIGIO = 29; + pub const SIGPOLL = 29; + pub const SIGPWR = 30; + pub const SIGSYS = 31; + pub const SIGUNUSED = SIGSYS; +}; pub const O_RDONLY = 0o0; pub const O_WRONLY = 0o1; @@ -419,7 +459,39 @@ pub const AF_QIPCRTR = PF_QIPCRTR; pub const AF_SMC = PF_SMC; pub const AF_MAX = PF_MAX; -pub usingnamespace if (!is_mips) +pub usingnamespace if (is_mips) + struct {} +else if (is_ppc or is_ppc64) + struct { + pub const SO_DEBUG = 1; + pub const SO_REUSEADDR = 2; + pub const SO_TYPE = 3; + pub const SO_ERROR = 4; + pub const SO_DONTROUTE = 5; + pub const SO_BROADCAST = 6; + pub const SO_SNDBUF = 7; + pub const SO_RCVBUF = 8; + pub const SO_KEEPALIVE = 9; + pub const SO_OOBINLINE = 10; + pub const SO_NO_CHECK = 11; + pub const SO_PRIORITY = 12; + pub const SO_LINGER = 13; + pub const SO_BSDCOMPAT = 14; + pub const SO_REUSEPORT = 15; + pub const SO_RCVLOWAT = 16; + pub const SO_SNDLOWAT = 17; + pub const SO_RCVTIMEO = 18; + pub const SO_SNDTIMEO = 19; + pub const SO_PASSCRED = 20; + pub const SO_PEERCRED = 21; + pub const SO_ACCEPTCONN = 30; + pub const SO_PEERSEC = 31; + pub const SO_SNDBUFFORCE = 32; + pub const SO_RCVBUFFORCE = 33; + pub const SO_PROTOCOL = 38; + pub const SO_DOMAIN = 39; + } +else struct { pub const SO_DEBUG = 1; pub const SO_REUSEADDR = 2; @@ -448,9 +520,7 @@ pub usingnamespace if (!is_mips) pub const SO_RCVBUFFORCE = 33; pub const SO_PROTOCOL = 38; pub const SO_DOMAIN = 39; - } -else - struct {}; + }; pub const SO_SECURITY_AUTHENTICATION = 22; pub const SO_SECURITY_ENCRYPTION_TRANSPORT = 23; diff --git a/zig/lib/std/os/bits/linux/arm64.zig b/zig/lib/std/os/bits/linux/arm64.zig index e373d978e1..9737a68de1 100644 --- a/zig/lib/std/os/bits/linux/arm64.zig +++ b/zig/lib/std/os/bits/linux/arm64.zig @@ -9,6 +9,7 @@ const std = @import("../../../std.zig"); const linux = std.os.linux; const socklen_t = linux.socklen_t; +const sockaddr = linux.sockaddr; const iovec = linux.iovec; const iovec_const = linux.iovec_const; const uid_t = linux.uid_t; diff --git a/zig/lib/std/os/bits/linux/errno-mips.zig b/zig/lib/std/os/bits/linux/errno-mips.zig index 2c74fa6f8c..2c22290288 100644 --- a/zig/lib/std/os/bits/linux/errno-mips.zig +++ b/zig/lib/std/os/bits/linux/errno-mips.zig @@ -3,6 +3,9 @@ // This file is part of [zig](https://ziglang.org/), which is MIT licensed. // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. + +// These are MIPS ABI compatible. + pub const EPERM = 1; pub const ENOENT = 2; pub const ESRCH = 3; @@ -37,6 +40,7 @@ pub const EMLINK = 31; pub const EPIPE = 32; pub const EDOM = 33; pub const ERANGE = 34; + pub const ENOMSG = 35; pub const EIDRM = 36; pub const ECHRNG = 37; diff --git a/zig/lib/std/os/bits/linux/errno-sparc.zig b/zig/lib/std/os/bits/linux/errno-sparc.zig new file mode 100644 index 0000000000..bbeabaaef0 --- /dev/null +++ b/zig/lib/std/os/bits/linux/errno-sparc.zig @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2021 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. + +// These match the SunOS error numbering scheme. + +pub const EPERM = 1; +pub const ENOENT = 2; +pub const ESRCH = 3; +pub const EINTR = 4; +pub const EIO = 5; +pub const ENXIO = 6; +pub const E2BIG = 7; +pub const ENOEXEC = 8; +pub const EBADF = 9; +pub const ECHILD = 10; +pub const EAGAIN = 11; +pub const ENOMEM = 12; +pub const EACCES = 13; +pub const EFAULT = 14; +pub const ENOTBLK = 15; +pub const EBUSY = 16; +pub const EEXIST = 17; +pub const EXDEV = 18; +pub const ENODEV = 19; +pub const ENOTDIR = 20; +pub const EISDIR = 21; +pub const EINVAL = 22; +pub const ENFILE = 23; +pub const EMFILE = 24; +pub const ENOTTY = 25; +pub const ETXTBSY = 26; +pub const EFBIG = 27; +pub const ENOSPC = 28; +pub const ESPIPE = 29; +pub const EROFS = 30; +pub const EMLINK = 31; +pub const EPIPE = 32; +pub const EDOM = 33; +pub const ERANGE = 34; + +pub const EWOULDBLOCK = EAGAIN; +pub const EINPROGRESS = 36; +pub const EALREADY = 37; +pub const ENOTSOCK = 38; +pub const EDESTADDRREQ = 39; +pub const EMSGSIZE = 40; +pub const EPROTOTYPE = 41; +pub const ENOPROTOOPT = 42; +pub const EPROTONOSUPPORT = 43; +pub const ESOCKTNOSUPPORT = 44; +pub const EOPNOTSUPP = 45; +pub const EPFNOSUPPORT = 46; +pub const EAFNOSUPPORT = 47; +pub const EADDRINUSE = 48; +pub const EADDRNOTAVAIL = 49; +pub const ENETDOWN = 50; +pub const ENETUNREACH = 51; +pub const ENETRESET = 52; +pub const ECONNABORTED = 53; +pub const ECONNRESET = 54; +pub const ENOBUFS = 55; +pub const EISCONN = 56; +pub const ENOTCONN = 57; +pub const ESHUTDOWN = 58; +pub const ETOOMANYREFS = 59; +pub const ETIMEDOUT = 60; +pub const ECONNREFUSED = 61; +pub const ELOOP = 62; +pub const ENAMETOOLONG = 63; +pub const EHOSTDOWN = 64; +pub const EHOSTUNREACH = 65; +pub const ENOTEMPTY = 66; +pub const EPROCLIM = 67; +pub const EUSERS = 68; +pub const EDQUOT = 69; +pub const ESTALE = 70; +pub const EREMOTE = 71; +pub const ENOSTR = 72; +pub const ETIME = 73; +pub const ENOSR = 74; +pub const ENOMSG = 75; +pub const EBADMSG = 76; +pub const EIDRM = 77; +pub const EDEADLK = 78; +pub const ENOLCK = 79; +pub const ENONET = 80; +pub const ERREMOTE = 81; +pub const ENOLINK = 82; +pub const EADV = 83; +pub const ESRMNT = 84; +pub const ECOMM = 85; +pub const EPROTO = 86; +pub const EMULTIHOP = 87; +pub const EDOTDOT = 88; +pub const EREMCHG = 89; +pub const ENOSYS = 90; +pub const ESTRPIPE = 91; +pub const EOVERFLOW = 92; +pub const EBADFD = 93; +pub const ECHRNG = 94; +pub const EL2NSYNC = 95; +pub const EL3HLT = 96; +pub const EL3RST = 97; +pub const ELNRNG = 98; +pub const EUNATCH = 99; +pub const ENOCSI = 100; +pub const EL2HLT = 101; +pub const EBADE = 102; +pub const EBADR = 103; +pub const EXFULL = 104; +pub const ENOANO = 105; +pub const EBADRQC = 106; +pub const EBADSLT = 107; +pub const EDEADLOCK = 108; +pub const EBFONT = 109; +pub const ELIBEXEC = 110; +pub const ENODATA = 111; +pub const ELIBBAD = 112; +pub const ENOPKG = 113; +pub const ELIBACC = 114; +pub const ENOTUNIQ = 115; +pub const ERESTART = 116; +pub const EUCLEAN = 117; +pub const ENOTNAM = 118; +pub const ENAVAIL = 119; +pub const EISNAM = 120; +pub const EREMOTEIO = 121; +pub const EILSEQ = 122; +pub const ELIBMAX = 123; +pub const ELIBSCN = 124; +pub const ENOMEDIUM = 125; +pub const EMEDIUMTYPE = 126; +pub const ECANCELED = 127; +pub const ENOKEY = 128; +pub const EKEYEXPIRED = 129; +pub const EKEYREVOKED = 130; +pub const EKEYREJECTED = 131; +pub const EOWNERDEAD = 132; +pub const ENOTRECOVERABLE = 133; +pub const ERFKILL = 134; +pub const EHWPOISON = 135; diff --git a/zig/lib/std/os/bits/linux/powerpc.zig b/zig/lib/std/os/bits/linux/powerpc.zig index baa6a57bcf..158a5dab3d 100644 --- a/zig/lib/std/os/bits/linux/powerpc.zig +++ b/zig/lib/std/os/bits/linux/powerpc.zig @@ -557,18 +557,10 @@ pub const kernel_stat = extern struct { size: off_t, blksize: blksize_t, blocks: blkcnt_t, - __atim32: timespec32, - __mtim32: timespec32, - __ctim32: timespec32, - __unused: [2]u32, atim: timespec, mtim: timespec, ctim: timespec, - - const timespec32 = extern struct { - tv_sec: i32, - tv_nsec: i32, - }; + __unused: [2]u32, pub fn atime(self: @This()) timespec { return self.atim; diff --git a/zig/lib/std/os/bits/linux/riscv64.zig b/zig/lib/std/os/bits/linux/riscv64.zig index 0cbdea415c..192a2a4f71 100644 --- a/zig/lib/std/os/bits/linux/riscv64.zig +++ b/zig/lib/std/os/bits/linux/riscv64.zig @@ -376,6 +376,11 @@ pub const timespec = extern struct { tv_nsec: isize, }; +pub const timeval = extern struct { + tv_sec: time_t, + tv_usec: i64, +}; + pub const Flock = extern struct { l_type: i16, l_whence: i16, diff --git a/zig/lib/std/os/bits/openbsd.zig b/zig/lib/std/os/bits/openbsd.zig index 8d1e74ec20..1aa5ce1c9c 100644 --- a/zig/lib/std/os/bits/openbsd.zig +++ b/zig/lib/std/os/bits/openbsd.zig @@ -805,7 +805,7 @@ comptime { if (@sizeOf(usize) == 4) std.debug.assert(@sizeOf(siginfo_t) == 128) else - // Take into account the padding between errno and data fields. + // Take into account the padding between errno and data fields. std.debug.assert(@sizeOf(siginfo_t) == 136); } diff --git a/zig/lib/std/os/bits/windows.zig b/zig/lib/std/os/bits/windows.zig index 28a6a251f8..00ca2a1532 100644 --- a/zig/lib/std/os/bits/windows.zig +++ b/zig/lib/std/os/bits/windows.zig @@ -22,6 +22,11 @@ pub const timespec = extern struct { tv_nsec: c_long, }; +pub const timeval = extern struct { + tv_sec: c_long, + tv_usec: c_long, +}; + pub const sig_atomic_t = c_int; /// maximum signal number + 1 diff --git a/zig/lib/std/os/linux.zig b/zig/lib/std/os/linux.zig index ed08438425..4a67ca7685 100644 --- a/zig/lib/std/os/linux.zig +++ b/zig/lib/std/os/linux.zig @@ -52,6 +52,7 @@ pub fn getauxval(index: usize) usize { // Some architectures (and some syscalls) require 64bit parameters to be passed // in a even-aligned register pair. const require_aligned_register_pair = + std.Target.current.cpu.arch.isPPC() or std.Target.current.cpu.arch.isMIPS() or std.Target.current.cpu.arch.isARM() or std.Target.current.cpu.arch.isThumb(); @@ -385,7 +386,7 @@ pub fn symlinkat(existing: [*:0]const u8, newfd: i32, newpath: [*:0]const u8) us } pub fn pread(fd: i32, buf: [*]u8, count: usize, offset: u64) usize { - if (@hasField(SYS, "pread64")) { + if (@hasField(SYS, "pread64") and usize_bits < 64) { const offset_halves = splitValue64(offset); if (require_aligned_register_pair) { return syscall6( @@ -408,8 +409,10 @@ pub fn pread(fd: i32, buf: [*]u8, count: usize, offset: u64) usize { ); } } else { + // Some architectures (eg. 64bit SPARC) pread is called pread64. + const S = if (!@hasField(SYS, "pread") and @hasField(SYS, "pread64")) .pread64 else .pread; return syscall4( - .pread, + S, @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), count, @@ -449,7 +452,7 @@ pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { } pub fn ftruncate(fd: i32, length: u64) usize { - if (@hasField(SYS, "ftruncate64")) { + if (@hasField(SYS, "ftruncate64") and usize_bits < 64) { const length_halves = splitValue64(length); if (require_aligned_register_pair) { return syscall4( @@ -477,7 +480,7 @@ pub fn ftruncate(fd: i32, length: u64) usize { } pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: u64) usize { - if (@hasField(SYS, "pwrite64")) { + if (@hasField(SYS, "pwrite64") and usize_bits < 64) { const offset_halves = splitValue64(offset); if (require_aligned_register_pair) { @@ -501,8 +504,10 @@ pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: u64) usize { ); } } else { + // Some architectures (eg. 64bit SPARC) pwrite is called pwrite64. + const S = if (!@hasField(SYS, "pwrite") and @hasField(SYS, "pwrite64")) .pwrite64 else .pwrite; return syscall4( - .pwrite, + S, @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), count, @@ -632,7 +637,7 @@ pub fn tkill(tid: pid_t, sig: i32) usize { } pub fn tgkill(tgid: pid_t, tid: pid_t, sig: i32) usize { - return syscall2(.tgkill, @bitCast(usize, @as(isize, tgid)), @bitCast(usize, @as(isize, tid)), @bitCast(usize, @as(isize, sig))); + return syscall3(.tgkill, @bitCast(usize, @as(isize, tgid)), @bitCast(usize, @as(isize, tid)), @bitCast(usize, @as(isize, sig))); } pub fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) usize { @@ -1387,6 +1392,53 @@ pub fn madvise(address: [*]u8, len: usize, advice: u32) usize { return syscall3(.madvise, @ptrToInt(address), len, advice); } +pub fn pidfd_open(pid: pid_t, flags: u32) usize { + return syscall2(.pidfd_open, @bitCast(usize, @as(isize, pid)), flags); +} + +pub fn pidfd_getfd(pidfd: fd_t, targetfd: fd_t, flags: u32) usize { + return syscall3( + .pidfd_getfd, + @bitCast(usize, @as(isize, pidfd)), + @bitCast(usize, @as(isize, targetfd)), + flags, + ); +} + +pub fn pidfd_send_signal(pidfd: fd_t, sig: i32, info: ?*siginfo_t, flags: u32) usize { + return syscall4( + .pidfd_send_signal, + @bitCast(usize, @as(isize, pidfd)), + @bitCast(usize, @as(isize, sig)), + @ptrToInt(info), + flags, + ); +} + +pub fn process_vm_readv(pid: pid_t, local: [*]const iovec, local_count: usize, remote: [*]const iovec, remote_count: usize, flags: usize) usize { + return syscall6( + .process_vm_readv, + @bitCast(usize, @as(isize, pid)), + @ptrToInt(local), + local_count, + @ptrToInt(remote), + remote_count, + flags, + ); +} + +pub fn process_vm_writev(pid: pid_t, local: [*]const iovec, local_count: usize, remote: [*]const iovec, remote_count: usize, flags: usize) usize { + return syscall6( + .process_vm_writev, + @bitCast(usize, @as(isize, pid)), + @ptrToInt(local), + local_count, + @ptrToInt(remote), + remote_count, + flags, + ); +} + test { if (builtin.os.tag == .linux) { _ = @import("linux/test.zig"); diff --git a/zig/lib/std/os/linux/bpf/btf.zig b/zig/lib/std/os/linux/bpf/btf.zig index b28f65945a..9ba3a0f942 100644 --- a/zig/lib/std/os/linux/bpf/btf.zig +++ b/zig/lib/std/os/linux/bpf/btf.zig @@ -6,7 +6,7 @@ const magic = 0xeb9f; const version = 1; -pub const ext = @import("ext.zig"); +pub const ext = @import("btf_ext.zig"); /// All offsets are in bytes relative to the end of this header pub const Header = packed struct { diff --git a/zig/lib/std/os/linux/tls.zig b/zig/lib/std/os/linux/tls.zig index 757e77bff3..4a36b0d485 100644 --- a/zig/lib/std/os/linux/tls.zig +++ b/zig/lib/std/os/linux/tls.zig @@ -248,7 +248,7 @@ fn initTLS() void { tls_data = @intToPtr([*]u8, img_base + phdr.p_vaddr)[0..phdr.p_filesz]; tls_data_alloc_size = phdr.p_memsz; } else { - tls_align_factor = @alignOf(*usize); + tls_align_factor = @alignOf(usize); tls_data = &[_]u8{}; tls_data_alloc_size = 0; } @@ -308,7 +308,7 @@ fn initTLS() void { } fn alignPtrCast(comptime T: type, ptr: [*]u8) callconv(.Inline) *T { - return @ptrCast(*T, @alignCast(@alignOf(*T), ptr)); + return @ptrCast(*T, @alignCast(@alignOf(T), ptr)); } /// Initializes all the fields of the static TLS area and returns the computed diff --git a/zig/lib/std/os/test.zig b/zig/lib/std/os/test.zig index c8cef38d5d..f0f0a7c988 100644 --- a/zig/lib/std/os/test.zig +++ b/zig/lib/std/os/test.zig @@ -261,6 +261,10 @@ test "linkat with different directories" { test "fstatat" { // enable when `fstat` and `fstatat` are implemented on Windows if (builtin.os.tag == .windows) return error.SkipZigTest; + if (builtin.os.tag == .freebsd and builtin.mode == .ReleaseFast) { + // https://github.com/ziglang/zig/issues/8538 + return error.SkipZigTest; + } var tmp = tmpDir(.{}); defer tmp.cleanup(); diff --git a/zig/lib/std/os/windows/user32.zig b/zig/lib/std/os/windows/user32.zig index 9a058f35c0..95ae5f0149 100644 --- a/zig/lib/std/os/windows/user32.zig +++ b/zig/lib/std/os/windows/user32.zig @@ -663,7 +663,7 @@ pub fn messageBoxA(hWnd: ?HWND, lpText: [*:0]const u8, lpCaption: [*:0]const u8, pub extern "user32" fn MessageBoxW(hWnd: ?HWND, lpText: [*:0]const u16, lpCaption: ?[*:0]const u16, uType: UINT) callconv(WINAPI) i32; pub var pfnMessageBoxW: @TypeOf(MessageBoxW) = undefined; pub fn messageBoxW(hWnd: ?HWND, lpText: [*:0]const u16, lpCaption: [*:0]const u16, uType: u32) !i32 { - const function = selectSymbol(pfnMessageBoxW, MessageBoxW, .win2k); + const function = selectSymbol(MessageBoxW, pfnMessageBoxW, .win2k); const value = function(hWnd, lpText, lpCaption, uType); if (value != 0) return value; switch (GetLastError()) { diff --git a/zig/lib/std/special/build_runner.zig b/zig/lib/std/special/build_runner.zig index 70aa3c8dc6..c6185ef093 100644 --- a/zig/lib/std/special/build_runner.zig +++ b/zig/lib/std/special/build_runner.zig @@ -82,9 +82,9 @@ pub fn main() !void { builder.verbose = true; } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { return usage(builder, false, stdout_stream); - } else if (mem.eql(u8, arg, "--prefix")) { + } else if (mem.eql(u8, arg, "-p") or mem.eql(u8, arg, "--prefix")) { install_prefix = nextArg(args, &arg_idx) orelse { - warn("Expected argument after --prefix\n\n", .{}); + warn("Expected argument after {s}\n\n", .{arg}); return usageAndErr(builder, false, stderr_stream); }; } else if (mem.eql(u8, arg, "--search-prefix")) { @@ -188,7 +188,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void \\General Options: \\ -h, --help Print this help and exit \\ --verbose Print commands before executing them - \\ --prefix [path] Override default install prefix + \\ -p, --prefix [path] Override default install prefix \\ --search-prefix [path] Add a path to look for binaries, libraries, headers \\ --color [auto|off|on] Enable or disable colored error messages \\ diff --git a/zig/lib/std/special/c.zig b/zig/lib/std/special/c.zig index f2f55e508e..c7084f3a11 100644 --- a/zig/lib/std/special/c.zig +++ b/zig/lib/std/special/c.zig @@ -85,7 +85,7 @@ test "strncpy" { var s1: [9:0]u8 = undefined; s1[0] = 0; - _ = strncpy(&s1, "foobarbaz", 9); + _ = strncpy(&s1, "foobarbaz", @sizeOf(@TypeOf(s1))); std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.spanZ(&s1)); } @@ -239,7 +239,7 @@ export fn memcmp(vl: ?[*]const u8, vr: ?[*]const u8, n: usize) callconv(.C) isiz return 0; } -test "test_memcmp" { +test "memcmp" { const base_arr = &[_]u8{ 1, 1, 1 }; const arr1 = &[_]u8{ 1, 1, 1 }; const arr2 = &[_]u8{ 1, 0, 1 }; @@ -263,7 +263,7 @@ export fn bcmp(vl: [*]allowzero const u8, vr: [*]allowzero const u8, n: usize) c return 0; } -test "test_bcmp" { +test "bcmp" { const base_arr = &[_]u8{ 1, 1, 1 }; const arr1 = &[_]u8{ 1, 1, 1 }; const arr2 = &[_]u8{ 1, 0, 1 }; @@ -614,23 +614,24 @@ fn clone() callconv(.Naked) void { \\ # Shuffle the arguments \\ mov 217, %%g1 \\ mov %%i2, %%o0 - \\ sub %%i1, 2047, %%o1 + \\ # Add some extra space for the initial frame + \\ sub %%i1, 176 + 2047, %%o1 \\ mov %%i4, %%o2 \\ mov %%i5, %%o3 - \\ ldx [%%fp + 192 - 2*8 + 2047], %%o4 + \\ ldx [%%fp + 0x8af], %%o4 \\ t 0x6d \\ bcs,pn %%xcc, 2f \\ nop - \\ # sparc64 returns the child pid in o0 and a flag telling - \\ # whether the process is the child in o1 + \\ # The child pid is returned in o0 while o1 tells if this + \\ # process is # the child (=1) or the parent (=0). \\ brnz %%o1, 1f \\ nop - \\ # This is the parent process, return the child pid + \\ # Parent process, return the child pid \\ mov %%o0, %%i0 \\ ret \\ restore \\1: - \\ # This is the child process + \\ # Child process, call func(arg) \\ mov %%g0, %%fp \\ call %%g2 \\ mov %%g3, %%o0 @@ -859,6 +860,85 @@ fn generic_fmod(comptime T: type, x: T, y: T) T { return @bitCast(T, ux); } +test "fmod, fmodf" { + inline for ([_]type{ f32, f64 }) |T| { + const nan_val = math.nan(T); + const inf_val = math.inf(T); + + std.testing.expect(isNan(generic_fmod(T, nan_val, 1.0))); + std.testing.expect(isNan(generic_fmod(T, 1.0, nan_val))); + std.testing.expect(isNan(generic_fmod(T, inf_val, 1.0))); + std.testing.expect(isNan(generic_fmod(T, 0.0, 0.0))); + std.testing.expect(isNan(generic_fmod(T, 1.0, 0.0))); + + std.testing.expectEqual(@as(T, 0.0), generic_fmod(T, 0.0, 2.0)); + std.testing.expectEqual(@as(T, -0.0), generic_fmod(T, -0.0, 2.0)); + + std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, 10.0)); + std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, -10.0)); + std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, 10.0)); + std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, -10.0)); + } +} + +fn generic_fmin(comptime T: type, x: T, y: T) T { + if (isNan(x)) + return y; + if (isNan(y)) + return x; + return if (x < y) x else y; +} + +export fn fminf(x: f32, y: f32) callconv(.C) f32 { + return generic_fmin(f32, x, y); +} + +export fn fmin(x: f64, y: f64) callconv(.C) f64 { + return generic_fmin(f64, x, y); +} + +test "fmin, fminf" { + inline for ([_]type{ f32, f64 }) |T| { + const nan_val = math.nan(T); + + std.testing.expect(isNan(generic_fmin(T, nan_val, nan_val))); + std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, nan_val, 1.0)); + std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, nan_val)); + + std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, 10.0)); + std.testing.expectEqual(@as(T, -1.0), generic_fmin(T, 1.0, -1.0)); + } +} + +fn generic_fmax(comptime T: type, x: T, y: T) T { + if (isNan(x)) + return y; + if (isNan(y)) + return x; + return if (x < y) y else x; +} + +export fn fmaxf(x: f32, y: f32) callconv(.C) f32 { + return generic_fmax(f32, x, y); +} + +export fn fmax(x: f64, y: f64) callconv(.C) f64 { + return generic_fmax(f64, x, y); +} + +test "fmax, fmaxf" { + inline for ([_]type{ f32, f64 }) |T| { + const nan_val = math.nan(T); + + std.testing.expect(isNan(generic_fmax(T, nan_val, nan_val))); + std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, nan_val, 1.0)); + std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, nan_val)); + + std.testing.expectEqual(@as(T, 10.0), generic_fmax(T, 1.0, 10.0)); + std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, -1.0)); + } +} + // NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound // behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are // potentially some edge cases remaining that are not handled in the same way. @@ -993,25 +1073,32 @@ export fn sqrt(x: f64) f64 { } test "sqrt" { - const epsilon = 0.000001; - - std.testing.expect(sqrt(0.0) == 0.0); - std.testing.expect(std.math.approxEqAbs(f64, sqrt(2.0), 1.414214, epsilon)); - std.testing.expect(std.math.approxEqAbs(f64, sqrt(3.6), 1.897367, epsilon)); - std.testing.expect(sqrt(4.0) == 2.0); - std.testing.expect(std.math.approxEqAbs(f64, sqrt(7.539840), 2.745877, epsilon)); - std.testing.expect(std.math.approxEqAbs(f64, sqrt(19.230934), 4.385309, epsilon)); - std.testing.expect(sqrt(64.0) == 8.0); - std.testing.expect(std.math.approxEqAbs(f64, sqrt(64.1), 8.006248, epsilon)); - std.testing.expect(std.math.approxEqAbs(f64, sqrt(8942.230469), 94.563367, epsilon)); + const V = [_]f64{ + 0.0, + 4.089288054930154, + 7.538757127071935, + 8.97780793672623, + 5.304443821913729, + 5.682408965311888, + 0.5846878579110049, + 3.650338664297043, + 0.3178091951800732, + 7.1505232436382835, + 3.6589165881946464, + }; + + // Note that @sqrt will either generate the sqrt opcode (if supported by the + // target ISA) or a call to `sqrtf` otherwise. + for (V) |val| + std.testing.expectEqual(@sqrt(val), sqrt(val)); } test "sqrt special" { std.testing.expect(std.math.isPositiveInf(sqrt(std.math.inf(f64)))); std.testing.expect(sqrt(0.0) == 0.0); std.testing.expect(sqrt(-0.0) == -0.0); - std.testing.expect(std.math.isNan(sqrt(-1.0))); - std.testing.expect(std.math.isNan(sqrt(std.math.nan(f64)))); + std.testing.expect(isNan(sqrt(-1.0))); + std.testing.expect(isNan(sqrt(std.math.nan(f64)))); } export fn sqrtf(x: f32) f32 { @@ -1091,23 +1178,30 @@ export fn sqrtf(x: f32) f32 { } test "sqrtf" { - const epsilon = 0.000001; - - std.testing.expect(sqrtf(0.0) == 0.0); - std.testing.expect(std.math.approxEqAbs(f32, sqrtf(2.0), 1.414214, epsilon)); - std.testing.expect(std.math.approxEqAbs(f32, sqrtf(3.6), 1.897367, epsilon)); - std.testing.expect(sqrtf(4.0) == 2.0); - std.testing.expect(std.math.approxEqAbs(f32, sqrtf(7.539840), 2.745877, epsilon)); - std.testing.expect(std.math.approxEqAbs(f32, sqrtf(19.230934), 4.385309, epsilon)); - std.testing.expect(sqrtf(64.0) == 8.0); - std.testing.expect(std.math.approxEqAbs(f32, sqrtf(64.1), 8.006248, epsilon)); - std.testing.expect(std.math.approxEqAbs(f32, sqrtf(8942.230469), 94.563370, epsilon)); + const V = [_]f32{ + 0.0, + 4.089288054930154, + 7.538757127071935, + 8.97780793672623, + 5.304443821913729, + 5.682408965311888, + 0.5846878579110049, + 3.650338664297043, + 0.3178091951800732, + 7.1505232436382835, + 3.6589165881946464, + }; + + // Note that @sqrt will either generate the sqrt opcode (if supported by the + // target ISA) or a call to `sqrtf` otherwise. + for (V) |val| + std.testing.expectEqual(@sqrt(val), sqrtf(val)); } test "sqrtf special" { std.testing.expect(std.math.isPositiveInf(sqrtf(std.math.inf(f32)))); std.testing.expect(sqrtf(0.0) == 0.0); std.testing.expect(sqrtf(-0.0) == -0.0); - std.testing.expect(std.math.isNan(sqrtf(-1.0))); - std.testing.expect(std.math.isNan(sqrtf(std.math.nan(f32)))); + std.testing.expect(isNan(sqrtf(-1.0))); + std.testing.expect(isNan(sqrtf(std.math.nan(f32)))); } diff --git a/zig/lib/std/special/compiler_rt.zig b/zig/lib/std/special/compiler_rt.zig index 3a7457a4fd..b0cf37553d 100644 --- a/zig/lib/std/special/compiler_rt.zig +++ b/zig/lib/std/special/compiler_rt.zig @@ -113,9 +113,11 @@ comptime { @export(@import("compiler_rt/extendXfYf2.zig").__extenddftf2, .{ .name = "__extenddftf2", .linkage = linkage }); @export(@import("compiler_rt/extendXfYf2.zig").__extendsftf2, .{ .name = "__extendsftf2", .linkage = linkage }); @export(@import("compiler_rt/extendXfYf2.zig").__extendhfsf2, .{ .name = "__extendhfsf2", .linkage = linkage }); + @export(@import("compiler_rt/extendXfYf2.zig").__extendhftf2, .{ .name = "__extendhftf2", .linkage = linkage }); @export(@import("compiler_rt/truncXfYf2.zig").__truncsfhf2, .{ .name = "__truncsfhf2", .linkage = linkage }); @export(@import("compiler_rt/truncXfYf2.zig").__truncdfhf2, .{ .name = "__truncdfhf2", .linkage = linkage }); + @export(@import("compiler_rt/truncXfYf2.zig").__trunctfhf2, .{ .name = "__trunctfhf2", .linkage = linkage }); @export(@import("compiler_rt/truncXfYf2.zig").__trunctfdf2, .{ .name = "__trunctfdf2", .linkage = linkage }); @export(@import("compiler_rt/truncXfYf2.zig").__trunctfsf2, .{ .name = "__trunctfsf2", .linkage = linkage }); @@ -296,7 +298,7 @@ comptime { @export(@import("compiler_rt/sparc.zig")._Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage }); } - if (builtin.arch == .powerpc or builtin.arch.isPPC64()) { + if ((builtin.arch == .powerpc or builtin.arch.isPPC64()) and !is_test) { @export(@import("compiler_rt/addXf3.zig").__addtf3, .{ .name = "__addkf3", .linkage = linkage }); @export(@import("compiler_rt/addXf3.zig").__subtf3, .{ .name = "__subkf3", .linkage = linkage }); @export(@import("compiler_rt/mulXf3.zig").__multf3, .{ .name = "__mulkf3", .linkage = linkage }); diff --git a/zig/lib/std/special/compiler_rt/extendXfYf2.zig b/zig/lib/std/special/compiler_rt/extendXfYf2.zig index c5b93fa51e..59be8441fa 100644 --- a/zig/lib/std/special/compiler_rt/extendXfYf2.zig +++ b/zig/lib/std/special/compiler_rt/extendXfYf2.zig @@ -23,6 +23,10 @@ pub fn __extendhfsf2(a: u16) callconv(.C) f32 { return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f32, f16, a }); } +pub fn __extendhftf2(a: u16) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f128, f16, a }); +} + pub fn __aeabi_h2f(arg: u16) callconv(.AAPCS) f32 { @setRuntimeSafety(false); return @call(.{ .modifier = .always_inline }, __extendhfsf2, .{arg}); diff --git a/zig/lib/std/special/compiler_rt/extendXfYf2_test.zig b/zig/lib/std/special/compiler_rt/extendXfYf2_test.zig index 6a3f69d8e9..f05d33eac3 100644 --- a/zig/lib/std/special/compiler_rt/extendXfYf2_test.zig +++ b/zig/lib/std/special/compiler_rt/extendXfYf2_test.zig @@ -4,9 +4,10 @@ // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. const builtin = @import("builtin"); -const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2; const __extendhfsf2 = @import("extendXfYf2.zig").__extendhfsf2; +const __extendhftf2 = @import("extendXfYf2.zig").__extendhftf2; const __extendsftf2 = @import("extendXfYf2.zig").__extendsftf2; +const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2; fn test__extenddftf2(a: f64, expectedHi: u64, expectedLo: u64) void { const x = __extenddftf2(a); @@ -161,3 +162,49 @@ fn makeNaN32(rand: u32) f32 { fn makeInf32() f32 { return @bitCast(f32, @as(u32, 0x7f800000)); } + +fn test__extendhftf2(a: u16, expectedHi: u64, expectedLo: u64) void { + const x = __extendhftf2(a); + + const rep = @bitCast(u128, x); + const hi = @intCast(u64, rep >> 64); + const lo = @truncate(u64, rep); + + if (hi == expectedHi and lo == expectedLo) + return; + + // test other possible NaN representation(signal NaN) + if (expectedHi == 0x7fff800000000000 and expectedLo == 0x0) { + if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and + ((hi & 0xffffffffffff) > 0 or lo > 0)) + { + return; + } + } + + @panic("__extendhftf2 test failure"); +} + +test "extendhftf2" { + // qNaN + test__extendhftf2(0x7e00, 0x7fff800000000000, 0x0); + // NaN + test__extendhftf2(0x7d00, 0x7fff400000000000, 0x0); + // inf + test__extendhftf2(0x7c00, 0x7fff000000000000, 0x0); + test__extendhftf2(0xfc00, 0xffff000000000000, 0x0); + // zero + test__extendhftf2(0x0000, 0x0000000000000000, 0x0); + test__extendhftf2(0x8000, 0x8000000000000000, 0x0); + // denormal + test__extendhftf2(0x0010, 0x3feb000000000000, 0x0); + test__extendhftf2(0x0001, 0x3fe7000000000000, 0x0); + test__extendhftf2(0x8001, 0xbfe7000000000000, 0x0); + + // pi + test__extendhftf2(0x4248, 0x4000920000000000, 0x0); + test__extendhftf2(0xc248, 0xc000920000000000, 0x0); + + test__extendhftf2(0x508c, 0x4004230000000000, 0x0); + test__extendhftf2(0x1bb7, 0x3ff6edc000000000, 0x0); +} diff --git a/zig/lib/std/special/compiler_rt/truncXfYf2.zig b/zig/lib/std/special/compiler_rt/truncXfYf2.zig index 470ac17c2c..e85aa363db 100644 --- a/zig/lib/std/special/compiler_rt/truncXfYf2.zig +++ b/zig/lib/std/special/compiler_rt/truncXfYf2.zig @@ -13,6 +13,10 @@ pub fn __truncdfhf2(a: f64) callconv(.C) u16 { return @bitCast(u16, @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f16, f64, a })); } +pub fn __trunctfhf2(a: f128) callconv(.C) u16 { + return @bitCast(u16, @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f16, f128, a })); +} + pub fn __trunctfsf2(a: f128) callconv(.C) f32 { return @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f32, f128, a }); } @@ -122,7 +126,7 @@ fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { if (shift > srcSigBits) { absResult = 0; } else { - const sticky: src_rep_t = significand << @intCast(SrcShift, srcBits - shift); + const sticky: src_rep_t = @boolToInt(significand << @intCast(SrcShift, srcBits - shift) != 0); const denormalizedSignificand: src_rep_t = significand >> @intCast(SrcShift, shift) | sticky; absResult = @intCast(dst_rep_t, denormalizedSignificand >> (srcSigBits - dstSigBits)); const roundBits: src_rep_t = denormalizedSignificand & roundMask; diff --git a/zig/lib/std/special/compiler_rt/truncXfYf2_test.zig b/zig/lib/std/special/compiler_rt/truncXfYf2_test.zig index 6426614b07..4fae5d1fc0 100644 --- a/zig/lib/std/special/compiler_rt/truncXfYf2_test.zig +++ b/zig/lib/std/special/compiler_rt/truncXfYf2_test.zig @@ -242,3 +242,59 @@ test "truncdfsf2" { // huge number becomes inf test__truncdfsf2(340282366920938463463374607431768211456.0, 0x7f800000); } + +const __trunctfhf2 = @import("truncXfYf2.zig").__trunctfhf2; + +fn test__trunctfhf2(a: f128, expected: u16) void { + const x = __trunctfhf2(a); + + const rep = @bitCast(u16, x); + if (rep == expected) { + return; + } + + @import("std").debug.warn("got 0x{x} wanted 0x{x}\n", .{ rep, expected }); + + @panic("__trunctfhf2 test failure"); +} + +test "trunctfhf2" { + // qNaN + test__trunctfhf2(@bitCast(f128, @as(u128, 0x7fff8000000000000000000000000000)), 0x7e00); + // NaN + test__trunctfhf2(@bitCast(f128, @as(u128, 0x7fff0000000000000000000000000001)), 0x7e00); + // inf + test__trunctfhf2(@bitCast(f128, @as(u128, 0x7fff0000000000000000000000000000)), 0x7c00); + test__trunctfhf2(-@bitCast(f128, @as(u128, 0x7fff0000000000000000000000000000)), 0xfc00); + // zero + test__trunctfhf2(0.0, 0x0); + test__trunctfhf2(-0.0, 0x8000); + + test__trunctfhf2(3.1415926535, 0x4248); + test__trunctfhf2(-3.1415926535, 0xc248); + test__trunctfhf2(0x1.987124876876324p+100, 0x7c00); + test__trunctfhf2(0x1.987124876876324p+12, 0x6e62); + test__trunctfhf2(0x1.0p+0, 0x3c00); + test__trunctfhf2(0x1.0p-14, 0x0400); + // denormal + test__trunctfhf2(0x1.0p-20, 0x0010); + test__trunctfhf2(0x1.0p-24, 0x0001); + test__trunctfhf2(-0x1.0p-24, 0x8001); + test__trunctfhf2(0x1.5p-25, 0x0001); + // and back to zero + test__trunctfhf2(0x1.0p-25, 0x0000); + test__trunctfhf2(-0x1.0p-25, 0x8000); + // max (precise) + test__trunctfhf2(65504.0, 0x7bff); + // max (rounded) + test__trunctfhf2(65519.0, 0x7bff); + // max (to +inf) + test__trunctfhf2(65520.0, 0x7c00); + test__trunctfhf2(65536.0, 0x7c00); + test__trunctfhf2(-65520.0, 0xfc00); + + test__trunctfhf2(0x1.23a2abb4a2ddee355f36789abcdep+5, 0x508f); + test__trunctfhf2(0x1.e3d3c45bd3abfd98b76a54cc321fp-9, 0x1b8f); + test__trunctfhf2(0x1.234eebb5faa678f4488693abcdefp+453, 0x7c00); + test__trunctfhf2(0x1.edcba9bb8c76a5a43dd21f334634p-43, 0x0); +} diff --git a/zig/lib/std/std.zig b/zig/lib/std/std.zig index 5f10def72e..940d1ab42e 100644 --- a/zig/lib/std/std.zig +++ b/zig/lib/std/std.zig @@ -88,6 +88,7 @@ pub const time = @import("time.zig"); pub const unicode = @import("unicode.zig"); pub const valgrind = @import("valgrind.zig"); pub const wasm = @import("wasm.zig"); +pub const x = @import("x.zig"); pub const zig = @import("zig.zig"); pub const start = @import("start.zig"); diff --git a/zig/lib/std/target.zig b/zig/lib/std/target.zig index 3372f617a8..e9185ef345 100644 --- a/zig/lib/std/target.zig +++ b/zig/lib/std/target.zig @@ -211,8 +211,9 @@ pub const Target = struct { /// If neither of these cases apply, a runtime check should be used to determine if the /// target supports a given OS feature. /// - /// Binaries built with a given maximum version will continue to function on newer operating system - /// versions. However, such a binary may not take full advantage of the newer operating system APIs. + /// Binaries built with a given maximum version will continue to function on newer + /// operating system versions. However, such a binary may not take full advantage of the + /// newer operating system APIs. /// /// See `Os.isAtLeast`. pub const VersionRange = union { @@ -260,7 +261,7 @@ pub const Target = struct { .freebsd => return .{ .semver = Version.Range{ .min = .{ .major = 12, .minor = 0 }, - .max = .{ .major = 12, .minor = 1 }, + .max = .{ .major = 13, .minor = 0 }, }, }, .macos => return .{ @@ -800,6 +801,13 @@ pub const Target = struct { }; } + pub fn isPPC(arch: Arch) bool { + return switch (arch) { + .powerpc, .powerpcle => true, + else => false, + }; + } + pub fn isPPC64(arch: Arch) bool { return switch (arch) { .powerpc64, .powerpc64le => true, @@ -1184,8 +1192,8 @@ pub const Target = struct { .mips, .mipsel => &mips.cpu.mips32, .mips64, .mips64el => &mips.cpu.mips64, .msp430 => &msp430.cpu.generic, - .powerpc => &powerpc.cpu.ppc32, - .powerpcle => &powerpc.cpu.ppc32, + .powerpc => &powerpc.cpu.ppc, + .powerpcle => &powerpc.cpu.ppc, .powerpc64 => &powerpc.cpu.ppc64, .powerpc64le => &powerpc.cpu.ppc64le, .amdgcn => &amdgpu.cpu.generic, diff --git a/zig/lib/std/target/aarch64.zig b/zig/lib/std/target/aarch64.zig index 03c2031207..a331d8a874 100644 --- a/zig/lib/std/target/aarch64.zig +++ b/zig/lib/std/target/aarch64.zig @@ -1621,6 +1621,16 @@ pub const cpu = struct { .apple_a7, }), }; + pub const emag = CpuModel{ + .name = "emag", + .llvm_name = null, + .features = featureSet(&[_]Feature{ + .crc, + .crypto, + .perfmon, + .v8a, + }), + }; pub const exynos_m1 = CpuModel{ .name = "exynos_m1", .llvm_name = null, @@ -1867,4 +1877,12 @@ pub const cpu = struct { .v8_2a, }), }; + pub const xgene1 = CpuModel{ + .name = "xgene1", + .llvm_name = null, + .features = featureSet(&[_]Feature{ + .perfmon, + .v8a, + }), + }; }; diff --git a/zig/lib/std/target/powerpc.zig b/zig/lib/std/target/powerpc.zig index 4e2200a47f..db4d5dccdc 100644 --- a/zig/lib/std/target/powerpc.zig +++ b/zig/lib/std/target/powerpc.zig @@ -751,13 +751,6 @@ pub const cpu = struct { .hard_float, }), }; - pub const ppc32 = CpuModel{ - .name = "ppc32", - .llvm_name = "ppc32", - .features = featureSet(&[_]Feature{ - .hard_float, - }), - }; pub const ppc64 = CpuModel{ .name = "ppc64", .llvm_name = "ppc64", diff --git a/zig/lib/std/unicode.zig b/zig/lib/std/unicode.zig index f9ad6e3eb5..9eed0f466f 100644 --- a/zig/lib/std/unicode.zig +++ b/zig/lib/std/unicode.zig @@ -206,7 +206,7 @@ pub fn utf8ValidateSlice(s: []const u8) bool { return false; } - if (utf8Decode(s[i .. i + cp_len])) |_| {} else |_| { + if (std.meta.isError(utf8Decode(s[i .. i + cp_len]))) { return false; } i += cp_len; diff --git a/zig/lib/std/x.zig b/zig/lib/std/x.zig new file mode 100644 index 0000000000..a123591470 --- /dev/null +++ b/zig/lib/std/x.zig @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2021 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. + +const std = @import("std.zig"); + +pub const os = struct { + pub const Socket = @import("x/os/Socket.zig"); + pub usingnamespace @import("x/os/net.zig"); +}; + +pub const net = struct { + pub const ip = @import("x/net/ip.zig"); + pub const tcp = @import("x/net/tcp.zig"); +}; + +test { + inline for (.{ os, net }) |module| { + std.testing.refAllDecls(module); + } +} diff --git a/zig/lib/std/x/net/ip.zig b/zig/lib/std/x/net/ip.zig new file mode 100644 index 0000000000..7f2d82d208 --- /dev/null +++ b/zig/lib/std/x/net/ip.zig @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2021 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. + +const std = @import("../../std.zig"); + +const fmt = std.fmt; + +const IPv4 = std.x.os.IPv4; +const IPv6 = std.x.os.IPv6; +const Socket = std.x.os.Socket; + +/// A generic IP abstraction. +const ip = @This(); + +/// A union of all eligible types of IP addresses. +pub const Address = union(enum) { + ipv4: IPv4.Address, + ipv6: IPv6.Address, + + /// Instantiate a new address with a IPv4 host and port. + pub fn initIPv4(host: IPv4, port: u16) Address { + return .{ .ipv4 = .{ .host = host, .port = port } }; + } + + /// Instantiate a new address with a IPv6 host and port. + pub fn initIPv6(host: IPv6, port: u16) Address { + return .{ .ipv6 = .{ .host = host, .port = port } }; + } + + /// Re-interpret a generic socket address into an IP address. + pub fn from(address: Socket.Address) ip.Address { + return switch (address) { + .ipv4 => |ipv4_address| .{ .ipv4 = ipv4_address }, + .ipv6 => |ipv6_address| .{ .ipv6 = ipv6_address }, + }; + } + + /// Re-interpret an IP address into a generic socket address. + pub fn into(self: ip.Address) Socket.Address { + return switch (self) { + .ipv4 => |ipv4_address| .{ .ipv4 = ipv4_address }, + .ipv6 => |ipv6_address| .{ .ipv6 = ipv6_address }, + }; + } + + /// Implements the `std.fmt.format` API. + pub fn format( + self: ip.Address, + comptime layout: []const u8, + opts: fmt.FormatOptions, + writer: anytype, + ) !void { + switch (self) { + .ipv4 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }), + .ipv6 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }), + } + } +}; diff --git a/zig/lib/std/x/net/tcp.zig b/zig/lib/std/x/net/tcp.zig new file mode 100644 index 0000000000..a4cc4a288c --- /dev/null +++ b/zig/lib/std/x/net/tcp.zig @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2021 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. + +const std = @import("../../std.zig"); + +const os = std.os; +const ip = std.x.net.ip; + +const fmt = std.fmt; +const mem = std.mem; +const builtin = std.builtin; +const testing = std.testing; + +const IPv4 = std.x.os.IPv4; +const IPv6 = std.x.os.IPv6; +const Socket = std.x.os.Socket; + +/// A generic TCP socket abstraction. +const tcp = @This(); + +/// A TCP client-address pair. +pub const Connection = struct { + client: tcp.Client, + address: ip.Address, + + /// Enclose a TCP client and address into a client-address pair. + pub fn from(conn: Socket.Connection) tcp.Connection { + return .{ + .client = tcp.Client.from(conn.socket), + .address = ip.Address.from(conn.address), + }; + } + + /// Unravel a TCP client-address pair into a socket-address pair. + pub fn into(self: tcp.Connection) Socket.Connection { + return .{ + .socket = self.client.socket, + .address = self.address.into(), + }; + } + + /// Closes the underlying client of the connection. + pub fn deinit(self: tcp.Connection) void { + self.client.deinit(); + } +}; + +/// Possible domains that a TCP client/listener may operate over. +pub const Domain = extern enum(u16) { + ip = os.AF_INET, + ipv6 = os.AF_INET6, +}; + +/// A TCP client. +pub const Client = struct { + socket: Socket, + + /// Opens a new client. + pub fn init(domain: tcp.Domain, flags: u32) !Client { + return Client{ + .socket = try Socket.init( + @enumToInt(domain), + os.SOCK_STREAM | flags, + os.IPPROTO_TCP, + ), + }; + } + + /// Enclose a TCP client over an existing socket. + pub fn from(socket: Socket) Client { + return Client{ .socket = socket }; + } + + /// Closes the client. + pub fn deinit(self: Client) void { + self.socket.deinit(); + } + + /// Shutdown either the read side, write side, or all sides of the client's underlying socket. + pub fn shutdown(self: Client, how: os.ShutdownHow) !void { + return self.socket.shutdown(how); + } + + /// Have the client attempt to the connect to an address. + pub fn connect(self: Client, address: ip.Address) !void { + return self.socket.connect(address.into()); + } + + /// Read data from the socket into the buffer provided. It returns the + /// number of bytes read into the buffer provided. + pub fn read(self: Client, buf: []u8) !usize { + return self.socket.read(buf); + } + + /// Read data from the socket into the buffer provided with a set of flags + /// specified. It returns the number of bytes read into the buffer provided. + pub fn recv(self: Client, buf: []u8, flags: u32) !usize { + return self.socket.recv(buf, flags); + } + + /// Write a buffer of data provided to the socket. It returns the number + /// of bytes that are written to the socket. + pub fn write(self: Client, buf: []const u8) !usize { + return self.socket.write(buf); + } + + /// Writes multiple I/O vectors to the socket. It returns the number + /// of bytes that are written to the socket. + pub fn writev(self: Client, buffers: []const os.iovec_const) !usize { + return self.socket.writev(buffers); + } + + /// Write a buffer of data provided to the socket with a set of flags specified. + /// It returns the number of bytes that are written to the socket. + pub fn send(self: Client, buf: []const u8, flags: u32) !usize { + return self.socket.send(buf, flags); + } + + /// Writes multiple I/O vectors with a prepended message header to the socket + /// with a set of flags specified. It returns the number of bytes that are + /// written to the socket. + pub fn sendmsg(self: Client, msg: os.msghdr_const, flags: u32) !usize { + return self.socket.sendmsg(msg, flags); + } + + /// Query and return the latest cached error on the client's underlying socket. + pub fn getError(self: Client) !void { + return self.socket.getError(); + } + + /// Query the read buffer size of the client's underlying socket. + pub fn getReadBufferSize(self: Client) !u32 { + return self.socket.getReadBufferSize(); + } + + /// Query the write buffer size of the client's underlying socket. + pub fn getWriteBufferSize(self: Client) !u32 { + return self.socket.getWriteBufferSize(); + } + + /// Query the address that the client's socket is locally bounded to. + pub fn getLocalAddress(self: Client) !ip.Address { + return ip.Address.from(try self.socket.getLocalAddress()); + } + + /// Disable Nagle's algorithm on a TCP socket. It returns `error.UnsupportedSocketOption` if + /// the host does not support sockets disabling Nagle's algorithm. + pub fn setNoDelay(self: Client, enabled: bool) !void { + if (comptime @hasDecl(os, "TCP_NODELAY")) { + const bytes = mem.asBytes(&@as(usize, @boolToInt(enabled))); + return os.setsockopt(self.socket.fd, os.IPPROTO_TCP, os.TCP_NODELAY, bytes); + } + return error.UnsupportedSocketOption; + } + + /// Set the write buffer size of the socket. + pub fn setWriteBufferSize(self: Client, size: u32) !void { + return self.socket.setWriteBufferSize(size); + } + + /// Set the read buffer size of the socket. + pub fn setReadBufferSize(self: Client, size: u32) !void { + return self.socket.setReadBufferSize(size); + } + + /// Set a timeout on the socket that is to occur if no messages are successfully written + /// to its bound destination after a specified number of milliseconds. A subsequent write + /// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded. + pub fn setWriteTimeout(self: Client, milliseconds: usize) !void { + return self.socket.setWriteTimeout(milliseconds); + } + + /// Set a timeout on the socket that is to occur if no messages are successfully read + /// from its bound destination after a specified number of milliseconds. A subsequent + /// read from the socket will thereafter return `error.WouldBlock` should the timeout be + /// exceeded. + pub fn setReadTimeout(self: Client, milliseconds: usize) !void { + return self.socket.setReadTimeout(milliseconds); + } +}; + +/// A TCP listener. +pub const Listener = struct { + socket: Socket, + + /// Opens a new listener. + pub fn init(domain: tcp.Domain, flags: u32) !Listener { + return Listener{ + .socket = try Socket.init( + @enumToInt(domain), + os.SOCK_STREAM | flags, + os.IPPROTO_TCP, + ), + }; + } + + /// Closes the listener. + pub fn deinit(self: Listener) void { + self.socket.deinit(); + } + + /// Shuts down the underlying listener's socket. The next subsequent call, or + /// a current pending call to accept() after shutdown is called will return + /// an error. + pub fn shutdown(self: Listener) !void { + return self.socket.shutdown(.recv); + } + + /// Binds the listener's socket to an address. + pub fn bind(self: Listener, address: ip.Address) !void { + return self.socket.bind(address.into()); + } + + /// Start listening for incoming connections. + pub fn listen(self: Listener, max_backlog_size: u31) !void { + return self.socket.listen(max_backlog_size); + } + + /// Accept a pending incoming connection queued to the kernel backlog + /// of the listener's socket. + pub fn accept(self: Listener, flags: u32) !tcp.Connection { + return tcp.Connection.from(try self.socket.accept(flags)); + } + + /// Query and return the latest cached error on the listener's underlying socket. + pub fn getError(self: Client) !void { + return self.socket.getError(); + } + + /// Query the address that the listener's socket is locally bounded to. + pub fn getLocalAddress(self: Listener) !ip.Address { + return ip.Address.from(try self.socket.getLocalAddress()); + } + + /// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if + /// the host does not support sockets listening the same address. + pub fn setReuseAddress(self: Listener, enabled: bool) !void { + return self.socket.setReuseAddress(enabled); + } + + /// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if + /// the host does not supports sockets listening on the same port. + pub fn setReusePort(self: Listener, enabled: bool) !void { + return self.socket.setReusePort(enabled); + } + + /// Enables TCP Fast Open (RFC 7413) on a TCP socket. It returns `error.UnsupportedSocketOption` if the host does not + /// support TCP Fast Open. + pub fn setFastOpen(self: Listener, enabled: bool) !void { + if (comptime @hasDecl(os, "TCP_FASTOPEN")) { + return os.setsockopt(self.socket.fd, os.IPPROTO_TCP, os.TCP_FASTOPEN, mem.asBytes(&@as(usize, @boolToInt(enabled)))); + } + return error.UnsupportedSocketOption; + } + + /// Enables TCP Quick ACK on a TCP socket to immediately send rather than delay ACKs when necessary. It returns + /// `error.UnsupportedSocketOption` if the host does not support TCP Quick ACK. + pub fn setQuickACK(self: Listener, enabled: bool) !void { + if (comptime @hasDecl(os, "TCP_QUICKACK")) { + return os.setsockopt(self.socket.fd, os.IPPROTO_TCP, os.TCP_QUICKACK, mem.asBytes(&@as(usize, @boolToInt(enabled)))); + } + return error.UnsupportedSocketOption; + } + + /// Set a timeout on the listener that is to occur if no new incoming connections come in + /// after a specified number of milliseconds. A subsequent accept call to the listener + /// will thereafter return `error.WouldBlock` should the timeout be exceeded. + pub fn setAcceptTimeout(self: Listener, milliseconds: usize) !void { + return self.socket.setReadTimeout(milliseconds); + } +}; + +test "tcp: create client/listener pair" { + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC); + defer listener.deinit(); + + try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0)); + try listener.listen(128); + + var binded_address = try listener.getLocalAddress(); + switch (binded_address) { + .ipv4 => |*ipv4| ipv4.host = IPv4.localhost, + .ipv6 => |*ipv6| ipv6.host = IPv6.localhost, + } + + const client = try tcp.Client.init(.ip, os.SOCK_CLOEXEC); + defer client.deinit(); + + try client.connect(binded_address); + + const conn = try listener.accept(os.SOCK_CLOEXEC); + defer conn.deinit(); +} + +test "tcp/client: set read timeout of 1 millisecond on blocking client" { + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC); + defer listener.deinit(); + + try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0)); + try listener.listen(128); + + var binded_address = try listener.getLocalAddress(); + switch (binded_address) { + .ipv4 => |*ipv4| ipv4.host = IPv4.localhost, + .ipv6 => |*ipv6| ipv6.host = IPv6.localhost, + } + + const client = try tcp.Client.init(.ip, os.SOCK_CLOEXEC); + defer client.deinit(); + + try client.connect(binded_address); + try client.setReadTimeout(1); + + const conn = try listener.accept(os.SOCK_CLOEXEC); + defer conn.deinit(); + + var buf: [1]u8 = undefined; + testing.expectError(error.WouldBlock, client.read(&buf)); +} + +test "tcp/listener: bind to unspecified ipv4 address" { + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC); + defer listener.deinit(); + + try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0)); + try listener.listen(128); + + const address = try listener.getLocalAddress(); + testing.expect(address == .ipv4); +} + +test "tcp/listener: bind to unspecified ipv6 address" { + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + const listener = try tcp.Listener.init(.ipv6, os.SOCK_CLOEXEC); + defer listener.deinit(); + + try listener.bind(ip.Address.initIPv6(IPv6.unspecified, 0)); + try listener.listen(128); + + const address = try listener.getLocalAddress(); + testing.expect(address == .ipv6); +} diff --git a/zig/lib/std/x/os/Socket.zig b/zig/lib/std/x/os/Socket.zig new file mode 100644 index 0000000000..3656899aea --- /dev/null +++ b/zig/lib/std/x/os/Socket.zig @@ -0,0 +1,295 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2021 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. + +const std = @import("../../std.zig"); +const net = @import("net.zig"); + +const os = std.os; +const fmt = std.fmt; +const mem = std.mem; +const time = std.time; + +/// A generic socket abstraction. +const Socket = @This(); + +/// A socket-address pair. +pub const Connection = struct { + socket: Socket, + address: Socket.Address, + + /// Enclose a socket and address into a socket-address pair. + pub fn from(socket: Socket, address: Socket.Address) Socket.Connection { + return .{ .socket = socket, .address = address }; + } +}; + +/// A generic socket address abstraction. It is safe to directly access and modify +/// the fields of a `Socket.Address`. +pub const Address = union(enum) { + ipv4: net.IPv4.Address, + ipv6: net.IPv6.Address, + + /// Instantiate a new address with a IPv4 host and port. + pub fn initIPv4(host: net.IPv4, port: u16) Socket.Address { + return .{ .ipv4 = .{ .host = host, .port = port } }; + } + + /// Instantiate a new address with a IPv6 host and port. + pub fn initIPv6(host: net.IPv6, port: u16) Socket.Address { + return .{ .ipv6 = .{ .host = host, .port = port } }; + } + + /// Parses a `sockaddr` into a generic socket address. + pub fn fromNative(address: *align(4) const os.sockaddr) Socket.Address { + switch (address.family) { + os.AF_INET => { + const info = @ptrCast(*const os.sockaddr_in, address); + const host = net.IPv4{ .octets = @bitCast([4]u8, info.addr) }; + const port = mem.bigToNative(u16, info.port); + return Socket.Address.initIPv4(host, port); + }, + os.AF_INET6 => { + const info = @ptrCast(*const os.sockaddr_in6, address); + const host = net.IPv6{ .octets = info.addr, .scope_id = info.scope_id }; + const port = mem.bigToNative(u16, info.port); + return Socket.Address.initIPv6(host, port); + }, + else => unreachable, + } + } + + /// Encodes a generic socket address into an extern union that may be reliably + /// casted into a `sockaddr` which may be passed into socket syscalls. + pub fn toNative(self: Socket.Address) extern union { + ipv4: os.sockaddr_in, + ipv6: os.sockaddr_in6, + } { + return switch (self) { + .ipv4 => |address| .{ + .ipv4 = .{ + .addr = @bitCast(u32, address.host.octets), + .port = mem.nativeToBig(u16, address.port), + }, + }, + .ipv6 => |address| .{ + .ipv6 = .{ + .addr = address.host.octets, + .port = mem.nativeToBig(u16, address.port), + .scope_id = address.host.scope_id, + .flowinfo = 0, + }, + }, + }; + } + + /// Returns the number of bytes that make up the `sockaddr` equivalent to the address. + pub fn getNativeSize(self: Socket.Address) u32 { + return switch (self) { + .ipv4 => @sizeOf(os.sockaddr_in), + .ipv6 => @sizeOf(os.sockaddr_in6), + }; + } + + /// Implements the `std.fmt.format` API. + pub fn format( + self: Socket.Address, + comptime layout: []const u8, + opts: fmt.FormatOptions, + writer: anytype, + ) !void { + switch (self) { + .ipv4 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }), + .ipv6 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }), + } + } +}; + +/// The underlying handle of a socket. +fd: os.socket_t, + +/// Open a new socket. +pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket { + return Socket{ .fd = try os.socket(domain, socket_type, protocol) }; +} + +/// Enclose a socket abstraction over an existing socket file descriptor. +pub fn from(fd: os.socket_t) Socket { + return Socket{ .fd = fd }; +} + +/// Closes the socket. +pub fn deinit(self: Socket) void { + os.closeSocket(self.fd); +} + +/// Shutdown either the read side, write side, or all side of the socket. +pub fn shutdown(self: Socket, how: os.ShutdownHow) !void { + return os.shutdown(self.fd, how); +} + +/// Binds the socket to an address. +pub fn bind(self: Socket, address: Socket.Address) !void { + return os.bind(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize()); +} + +/// Start listening for incoming connections on the socket. +pub fn listen(self: Socket, max_backlog_size: u31) !void { + return os.listen(self.fd, max_backlog_size); +} + +/// Have the socket attempt to the connect to an address. +pub fn connect(self: Socket, address: Socket.Address) !void { + return os.connect(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize()); +} + +/// Accept a pending incoming connection queued to the kernel backlog +/// of the socket. +pub fn accept(self: Socket, flags: u32) !Socket.Connection { + var address: os.sockaddr = undefined; + var address_len: u32 = @sizeOf(os.sockaddr); + + const socket = Socket{ .fd = try os.accept(self.fd, &address, &address_len, flags) }; + const socket_address = Socket.Address.fromNative(@alignCast(4, &address)); + + return Socket.Connection.from(socket, socket_address); +} + +/// Read data from the socket into the buffer provided. It returns the +/// number of bytes read into the buffer provided. +pub fn read(self: Socket, buf: []u8) !usize { + return os.read(self.fd, buf); +} + +/// Read data from the socket into the buffer provided with a set of flags +/// specified. It returns the number of bytes read into the buffer provided. +pub fn recv(self: Socket, buf: []u8, flags: u32) !usize { + return os.recv(self.fd, buf, flags); +} + +/// Write a buffer of data provided to the socket. It returns the number +/// of bytes that are written to the socket. +pub fn write(self: Socket, buf: []const u8) !usize { + return os.write(self.fd, buf); +} + +/// Writes multiple I/O vectors to the socket. It returns the number +/// of bytes that are written to the socket. +pub fn writev(self: Socket, buffers: []const os.iovec_const) !usize { + return os.writev(self.fd, buffers); +} + +/// Write a buffer of data provided to the socket with a set of flags specified. +/// It returns the number of bytes that are written to the socket. +pub fn send(self: Socket, buf: []const u8, flags: u32) !usize { + return os.send(self.fd, buf, flags); +} + +/// Writes multiple I/O vectors with a prepended message header to the socket +/// with a set of flags specified. It returns the number of bytes that are +/// written to the socket. +pub fn sendmsg(self: Socket, msg: os.msghdr_const, flags: u32) !usize { + return os.sendmsg(self.fd, msg, flags); +} + +/// Query the address that the socket is locally bounded to. +pub fn getLocalAddress(self: Socket) !Socket.Address { + var address: os.sockaddr = undefined; + var address_len: u32 = @sizeOf(os.sockaddr); + try os.getsockname(self.fd, &address, &address_len); + return Socket.Address.fromNative(@alignCast(4, &address)); +} + +/// Query and return the latest cached error on the socket. +pub fn getError(self: Socket) !void { + return os.getsockoptError(self.fd); +} + +/// Query the read buffer size of the socket. +pub fn getReadBufferSize(self: Socket) !u32 { + var value: u32 = undefined; + var value_len: u32 = @sizeOf(u32); + + const rc = os.system.getsockopt(self.fd, os.SOL_SOCKET, os.SO_RCVBUF, mem.asBytes(&value), &value_len); + return switch (os.errno(rc)) { + 0 => value, + os.EBADF => error.BadFileDescriptor, + os.EFAULT => error.InvalidAddressSpace, + os.EINVAL => error.InvalidSocketOption, + os.ENOPROTOOPT => error.UnknownSocketOption, + os.ENOTSOCK => error.NotASocket, + else => |err| os.unexpectedErrno(err), + }; +} + +/// Query the write buffer size of the socket. +pub fn getWriteBufferSize(self: Socket) !u32 { + var value: u32 = undefined; + var value_len: u32 = @sizeOf(u32); + + const rc = os.system.getsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&value), &value_len); + return switch (os.errno(rc)) { + 0 => value, + os.EBADF => error.BadFileDescriptor, + os.EFAULT => error.InvalidAddressSpace, + os.EINVAL => error.InvalidSocketOption, + os.ENOPROTOOPT => error.UnknownSocketOption, + os.ENOTSOCK => error.NotASocket, + else => |err| os.unexpectedErrno(err), + }; +} + +/// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if +/// the host does not support sockets listening the same address. +pub fn setReuseAddress(self: Socket, enabled: bool) !void { + if (comptime @hasDecl(os, "SO_REUSEADDR")) { + return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_REUSEADDR, mem.asBytes(&@as(usize, @boolToInt(enabled)))); + } + return error.UnsupportedSocketOption; +} + +/// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if +/// the host does not supports sockets listening on the same port. +pub fn setReusePort(self: Socket, enabled: bool) !void { + if (comptime @hasDecl(os, "SO_REUSEPORT")) { + return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_REUSEPORT, mem.asBytes(&@as(usize, @boolToInt(enabled)))); + } + return error.UnsupportedSocketOption; +} + +/// Set the write buffer size of the socket. +pub fn setWriteBufferSize(self: Socket, size: u32) !void { + return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&size)); +} + +/// Set the read buffer size of the socket. +pub fn setReadBufferSize(self: Socket, size: u32) !void { + return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_RCVBUF, mem.asBytes(&size)); +} + +/// Set a timeout on the socket that is to occur if no messages are successfully written +/// to its bound destination after a specified number of milliseconds. A subsequent write +/// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded. +pub fn setWriteTimeout(self: Socket, milliseconds: usize) !void { + const timeout = os.timeval{ + .tv_sec = @intCast(i32, milliseconds / time.ms_per_s), + .tv_usec = @intCast(i32, (milliseconds % time.ms_per_s) * time.us_per_ms), + }; + + return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDTIMEO, mem.asBytes(&timeout)); +} + +/// Set a timeout on the socket that is to occur if no messages are successfully read +/// from its bound destination after a specified number of milliseconds. A subsequent +/// read from the socket will thereafter return `error.WouldBlock` should the timeout be +/// exceeded. +pub fn setReadTimeout(self: Socket, milliseconds: usize) !void { + const timeout = os.timeval{ + .tv_sec = @intCast(i32, milliseconds / time.ms_per_s), + .tv_usec = @intCast(i32, (milliseconds % time.ms_per_s) * time.us_per_ms), + }; + + return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_RCVTIMEO, mem.asBytes(&timeout)); +} diff --git a/zig/lib/std/x/os/net.zig b/zig/lib/std/x/os/net.zig new file mode 100644 index 0000000000..a8d9f095d3 --- /dev/null +++ b/zig/lib/std/x/os/net.zig @@ -0,0 +1,567 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2021 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. + +const std = @import("../../std.zig"); + +const os = std.os; +const fmt = std.fmt; +const mem = std.mem; +const math = std.math; +const builtin = std.builtin; +const testing = std.testing; + +/// Resolves a network interface name into a scope/zone ID. It returns +/// an error if either resolution fails, or if the interface name is +/// too long. +pub fn resolveScopeID(name: []const u8) !u32 { + if (comptime @hasDecl(os, "IFNAMESIZE")) { + if (name.len >= os.IFNAMESIZE - 1) return error.NameTooLong; + + const fd = try os.socket(os.AF_UNIX, os.SOCK_DGRAM, 0); + defer os.closeSocket(fd); + + var f: os.ifreq = undefined; + mem.copy(u8, &f.ifrn.name, name); + f.ifrn.name[name.len] = 0; + + try os.ioctl_SIOCGIFINDEX(fd, &f); + + return @bitCast(u32, f.ifru.ivalue); + } + return error.Unsupported; +} + +/// An IPv4 address comprised of 4 bytes. +pub const IPv4 = extern struct { + /// A IPv4 host-port pair. + pub const Address = extern struct { + host: IPv4, + port: u16, + }; + + /// Octets of a IPv4 address designating the local host. + pub const localhost_octets = [_]u8{ 127, 0, 0, 1 }; + + /// The IPv4 address of the local host. + pub const localhost: IPv4 = .{ .octets = localhost_octets }; + + /// Octets of an unspecified IPv4 address. + pub const unspecified_octets = [_]u8{0} ** 4; + + /// An unspecified IPv4 address. + pub const unspecified: IPv4 = .{ .octets = unspecified_octets }; + + /// Octets of a broadcast IPv4 address. + pub const broadcast_octets = [_]u8{255} ** 4; + + /// An IPv4 broadcast address. + pub const broadcast: IPv4 = .{ .octets = broadcast_octets }; + + /// The prefix octet pattern of a link-local IPv4 address. + pub const link_local_prefix = [_]u8{ 169, 254 }; + + /// The prefix octet patterns of IPv4 addresses intended for + /// documentation. + pub const documentation_prefixes = [_][]const u8{ + &[_]u8{ 192, 0, 2 }, + &[_]u8{ 198, 51, 100 }, + &[_]u8{ 203, 0, 113 }, + }; + + octets: [4]u8, + + /// Returns whether or not the two addresses are equal to, less than, or + /// greater than each other. + pub fn cmp(self: IPv4, other: IPv4) math.Order { + return mem.order(u8, &self.octets, &other.octets); + } + + /// Returns true if both addresses are semantically equivalent. + pub fn eql(self: IPv4, other: IPv4) bool { + return mem.eql(u8, &self.octets, &other.octets); + } + + /// Returns true if the address is a loopback address. + pub fn isLoopback(self: IPv4) bool { + return self.octets[0] == 127; + } + + /// Returns true if the address is an unspecified IPv4 address. + pub fn isUnspecified(self: IPv4) bool { + return mem.eql(u8, &self.octets, &unspecified_octets); + } + + /// Returns true if the address is a private IPv4 address. + pub fn isPrivate(self: IPv4) bool { + return self.octets[0] == 10 or + (self.octets[0] == 172 and self.octets[1] >= 16 and self.octets[1] <= 31) or + (self.octets[0] == 192 and self.octets[1] == 168); + } + + /// Returns true if the address is a link-local IPv4 address. + pub fn isLinkLocal(self: IPv4) bool { + return mem.startsWith(u8, &self.octets, &link_local_prefix); + } + + /// Returns true if the address is a multicast IPv4 address. + pub fn isMulticast(self: IPv4) bool { + return self.octets[0] >= 224 and self.octets[0] <= 239; + } + + /// Returns true if the address is a IPv4 broadcast address. + pub fn isBroadcast(self: IPv4) bool { + return mem.eql(u8, &self.octets, &broadcast_octets); + } + + /// Returns true if the address is in a range designated for documentation. Refer + /// to IETF RFC 5737 for more details. + pub fn isDocumentation(self: IPv4) bool { + inline for (documentation_prefixes) |prefix| { + if (mem.startsWith(u8, &self.octets, prefix)) { + return true; + } + } + return false; + } + + /// Implements the `std.fmt.format` API. + pub fn format( + self: IPv4, + comptime layout: []const u8, + opts: fmt.FormatOptions, + writer: anytype, + ) !void { + if (comptime layout.len != 0 and layout[0] != 's') { + @compileError("Unsupported format specifier for IPv4 type '" ++ layout ++ "'."); + } + + try fmt.format(writer, "{}.{}.{}.{}", .{ + self.octets[0], + self.octets[1], + self.octets[2], + self.octets[3], + }); + } + + /// Set of possible errors that may encountered when parsing an IPv4 + /// address. + pub const ParseError = error{ + UnexpectedEndOfOctet, + TooManyOctets, + OctetOverflow, + UnexpectedToken, + IncompleteAddress, + }; + + /// Parses an arbitrary IPv4 address. + pub fn parse(buf: []const u8) ParseError!IPv4 { + var octets: [4]u8 = undefined; + var octet: u8 = 0; + + var index: u8 = 0; + var saw_any_digits: bool = false; + + for (buf) |c| { + switch (c) { + '.' => { + if (!saw_any_digits) return error.UnexpectedEndOfOctet; + if (index == 3) return error.TooManyOctets; + octets[index] = octet; + index += 1; + octet = 0; + saw_any_digits = false; + }, + '0'...'9' => { + saw_any_digits = true; + octet = math.mul(u8, octet, 10) catch return error.OctetOverflow; + octet = math.add(u8, octet, c - '0') catch return error.OctetOverflow; + }, + else => return error.UnexpectedToken, + } + } + + if (index == 3 and saw_any_digits) { + octets[index] = octet; + return IPv4{ .octets = octets }; + } + + return error.IncompleteAddress; + } + + /// Maps the address to its IPv6 equivalent. In most cases, you would + /// want to map the address to its IPv6 equivalent rather than directly + /// re-interpreting the address. + pub fn mapToIPv6(self: IPv4) IPv6 { + var octets: [16]u8 = undefined; + mem.copy(u8, octets[0..12], &IPv6.v4_mapped_prefix); + mem.copy(u8, octets[12..], &self.octets); + return IPv6{ .octets = octets, .scope_id = IPv6.no_scope_id }; + } + + /// Directly re-interprets the address to its IPv6 equivalent. In most + /// cases, you would want to map the address to its IPv6 equivalent rather + /// than directly re-interpreting the address. + pub fn toIPv6(self: IPv4) IPv6 { + var octets: [16]u8 = undefined; + mem.set(u8, octets[0..12], 0); + mem.copy(u8, octets[12..], &self.octets); + return IPv6{ .octets = octets, .scope_id = IPv6.no_scope_id }; + } +}; + +/// An IPv6 address comprised of 16 bytes for an address, and 4 bytes +/// for a scope ID; cumulatively summing to 20 bytes in total. +pub const IPv6 = extern struct { + /// A IPv6 host-port pair. + pub const Address = extern struct { + host: IPv6, + port: u16, + }; + + /// Octets of a IPv6 address designating the local host. + pub const localhost_octets = [_]u8{0} ** 15 ++ [_]u8{0x01}; + + /// The IPv6 address of the local host. + pub const localhost: IPv6 = .{ + .octets = localhost_octets, + .scope_id = no_scope_id, + }; + + /// Octets of an unspecified IPv6 address. + pub const unspecified_octets = [_]u8{0} ** 16; + + /// An unspecified IPv6 address. + pub const unspecified: IPv6 = .{ + .octets = unspecified_octets, + .scope_id = no_scope_id, + }; + + /// The prefix of a IPv6 address that is mapped to a IPv4 address. + pub const v4_mapped_prefix = [_]u8{0} ** 10 ++ [_]u8{0xFF} ** 2; + + /// A marker value used to designate an IPv6 address with no + /// associated scope ID. + pub const no_scope_id = math.maxInt(u32); + + octets: [16]u8, + scope_id: u32, + + /// Returns whether or not the two addresses are equal to, less than, or + /// greater than each other. + pub fn cmp(self: IPv6, other: IPv6) math.Order { + return switch (mem.order(u8, self.octets, other.octets)) { + .eq => math.order(self.scope_id, other.scope_id), + else => |order| order, + }; + } + + /// Returns true if both addresses are semantically equivalent. + pub fn eql(self: IPv6, other: IPv6) bool { + return self.scope_id == other.scope_id and mem.eql(u8, &self.octets, &other.octets); + } + + /// Returns true if the address is an unspecified IPv6 address. + pub fn isUnspecified(self: IPv6) bool { + return mem.eql(u8, &self.octets, &unspecified_octets); + } + + /// Returns true if the address is a loopback address. + pub fn isLoopback(self: IPv6) bool { + return mem.eql(u8, self.octets[0..3], &[_]u8{ 0, 0, 0 }) and + mem.eql(u8, self.octets[12..], &[_]u8{ 0, 0, 0, 1 }); + } + + /// Returns true if the address maps to an IPv4 address. + pub fn mapsToIPv4(self: IPv6) bool { + return mem.startsWith(u8, &self.octets, &v4_mapped_prefix); + } + + /// Returns an IPv4 address representative of the address should + /// it the address be mapped to an IPv4 address. It returns null + /// otherwise. + pub fn toIPv4(self: IPv6) ?IPv4 { + if (!self.mapsToIPv4()) return null; + return IPv4{ .octets = self.octets[12..][0..4].* }; + } + + /// Returns true if the address is a multicast IPv6 address. + pub fn isMulticast(self: IPv6) bool { + return self.octets[0] == 0xFF; + } + + /// Returns true if the address is a unicast link local IPv6 address. + pub fn isLinkLocal(self: IPv6) bool { + return self.octets[0] == 0xFE and self.octets[1] & 0xC0 == 0x80; + } + + /// Returns true if the address is a deprecated unicast site local + /// IPv6 address. Refer to IETF RFC 3879 for more details as to + /// why they are deprecated. + pub fn isSiteLocal(self: IPv6) bool { + return self.octets[0] == 0xFE and self.octets[1] & 0xC0 == 0xC0; + } + + /// IPv6 multicast address scopes. + pub const Scope = enum(u8) { + interface = 1, + link = 2, + realm = 3, + admin = 4, + site = 5, + organization = 8, + global = 14, + unknown = 0xFF, + }; + + /// Returns the multicast scope of the address. + pub fn scope(self: IPv6) Scope { + if (!self.isMulticast()) return .unknown; + + return switch (self.octets[0] & 0x0F) { + 1 => .interface, + 2 => .link, + 3 => .realm, + 4 => .admin, + 5 => .site, + 8 => .organization, + 14 => .global, + else => .unknown, + }; + } + + /// Implements the `std.fmt.format` API. Specifying 'x' or 's' formats the + /// address lower-cased octets, while specifying 'X' or 'S' formats the + /// address using upper-cased ASCII octets. + /// + /// The default specifier is 'x'. + pub fn format( + self: IPv6, + comptime layout: []const u8, + opts: fmt.FormatOptions, + writer: anytype, + ) !void { + comptime const specifier = &[_]u8{if (layout.len == 0) 'x' else switch (layout[0]) { + 'x', 'X' => |specifier| specifier, + 's' => 'x', + 'S' => 'X', + else => @compileError("Unsupported format specifier for IPv6 type '" ++ layout ++ "'."), + }}; + + if (mem.startsWith(u8, &self.octets, &v4_mapped_prefix)) { + return fmt.format(writer, "::{" ++ specifier ++ "}{" ++ specifier ++ "}:{}.{}.{}.{}", .{ + 0xFF, + 0xFF, + self.octets[12], + self.octets[13], + self.octets[14], + self.octets[15], + }); + } + + const zero_span = span: { + var i: usize = 0; + while (i < self.octets.len) : (i += 2) { + if (self.octets[i] == 0 and self.octets[i + 1] == 0) break; + } else break :span .{ .from = 0, .to = 0 }; + + const from = i; + + while (i < self.octets.len) : (i += 2) { + if (self.octets[i] != 0 or self.octets[i + 1] != 0) break; + } + + break :span .{ .from = from, .to = i }; + }; + + var i: usize = 0; + while (i != 16) : (i += 2) { + if (zero_span.from != zero_span.to and i == zero_span.from) { + try writer.writeAll("::"); + } else if (i >= zero_span.from and i < zero_span.to) {} else { + if (i != 0 and i != zero_span.to) try writer.writeAll(":"); + + const val = @as(u16, self.octets[i]) << 8 | self.octets[i + 1]; + try fmt.formatIntValue(val, specifier, .{}, writer); + } + } + + if (self.scope_id != no_scope_id and self.scope_id != 0) { + try fmt.format(writer, "%{d}", .{self.scope_id}); + } + } + + /// Set of possible errors that may encountered when parsing an IPv6 + /// address. + pub const ParseError = error{ + MalformedV4Mapping, + BadScopeID, + } || IPv4.ParseError; + + /// Parses an arbitrary IPv6 address, including link-local addresses. + pub fn parse(buf: []const u8) ParseError!IPv6 { + if (mem.lastIndexOfScalar(u8, buf, '%')) |index| { + const ip_slice = buf[0..index]; + const scope_id_slice = buf[index + 1 ..]; + + if (scope_id_slice.len == 0) return error.BadScopeID; + + const scope_id: u32 = switch (scope_id_slice[0]) { + '0'...'9' => fmt.parseInt(u32, scope_id_slice, 10), + else => resolveScopeID(scope_id_slice), + } catch return error.BadScopeID; + + return parseWithScopeID(ip_slice, scope_id); + } + + return parseWithScopeID(buf, no_scope_id); + } + + /// Parses an IPv6 address with a pre-specified scope ID. Presumes + /// that the address is not a link-local address. + pub fn parseWithScopeID(buf: []const u8, scope_id: u32) ParseError!IPv6 { + var octets: [16]u8 = undefined; + var octet: u16 = 0; + var tail: [16]u8 = undefined; + + var out: []u8 = &octets; + var index: u8 = 0; + + var saw_any_digits: bool = false; + var abbrv: bool = false; + + for (buf) |c, i| { + switch (c) { + ':' => { + if (!saw_any_digits) { + if (abbrv) return error.UnexpectedToken; + if (i != 0) abbrv = true; + mem.set(u8, out[index..], 0); + out = &tail; + index = 0; + continue; + } + if (index == 14) return error.TooManyOctets; + + out[index] = @truncate(u8, octet >> 8); + index += 1; + out[index] = @truncate(u8, octet); + index += 1; + + octet = 0; + saw_any_digits = false; + }, + '.' => { + if (!abbrv or out[0] != 0xFF and out[1] != 0xFF) { + return error.MalformedV4Mapping; + } + const start_index = mem.lastIndexOfScalar(u8, buf[0..i], ':').? + 1; + const v4 = try IPv4.parse(buf[start_index..]); + octets[10] = 0xFF; + octets[11] = 0xFF; + mem.copy(u8, octets[12..], &v4.octets); + + return IPv6{ .octets = octets, .scope_id = scope_id }; + }, + else => { + saw_any_digits = true; + const digit = fmt.charToDigit(c, 16) catch return error.UnexpectedToken; + octet = math.mul(u16, octet, 16) catch return error.OctetOverflow; + octet = math.add(u16, octet, digit) catch return error.OctetOverflow; + }, + } + } + + if (!saw_any_digits and !abbrv) { + return error.IncompleteAddress; + } + + if (index == 14) { + out[14] = @truncate(u8, octet >> 8); + out[15] = @truncate(u8, octet); + } else { + out[index] = @truncate(u8, octet >> 8); + index += 1; + out[index] = @truncate(u8, octet); + index += 1; + mem.copy(u8, octets[16 - index ..], out[0..index]); + } + + return IPv6{ .octets = octets, .scope_id = scope_id }; + } +}; + +test { + testing.refAllDecls(@This()); +} + +test "ip: convert to and from ipv6" { + try testing.expectFmt("::7f00:1", "{}", .{IPv4.localhost.toIPv6()}); + testing.expect(!IPv4.localhost.toIPv6().mapsToIPv4()); + + try testing.expectFmt("::ffff:127.0.0.1", "{}", .{IPv4.localhost.mapToIPv6()}); + testing.expect(IPv4.localhost.mapToIPv6().mapsToIPv4()); + + testing.expect(IPv4.localhost.toIPv6().toIPv4() == null); + try testing.expectFmt("127.0.0.1", "{}", .{IPv4.localhost.mapToIPv6().toIPv4()}); +} + +test "ipv4: parse & format" { + const cases = [_][]const u8{ + "0.0.0.0", + "255.255.255.255", + "1.2.3.4", + "123.255.0.91", + "127.0.0.1", + }; + + for (cases) |case| { + try testing.expectFmt(case, "{}", .{try IPv4.parse(case)}); + } +} + +test "ipv6: parse & format" { + const inputs = [_][]const u8{ + "FF01:0:0:0:0:0:0:FB", + "FF01::Fb", + "::1", + "::", + "2001:db8::", + "::1234:5678", + "2001:db8::1234:5678", + "::ffff:123.5.123.5", + }; + + const outputs = [_][]const u8{ + "ff01::fb", + "ff01::fb", + "::1", + "::", + "2001:db8::", + "::1234:5678", + "2001:db8::1234:5678", + "::ffff:123.5.123.5", + }; + + for (inputs) |input, i| { + try testing.expectFmt(outputs[i], "{}", .{try IPv6.parse(input)}); + } +} + +test "ipv6: parse & format addresses with scope ids" { + if (!@hasDecl(os, "IFNAMESIZE")) return error.SkipZigTest; + + const inputs = [_][]const u8{ + "FF01::FB%lo", + }; + + const outputs = [_][]const u8{ + "ff01::fb%1", + }; + + for (inputs) |input, i| { + try testing.expectFmt(outputs[i], "{}", .{try IPv6.parse(input)}); + } +} diff --git a/zig/lib/std/zig/ast.zig b/zig/lib/std/zig/ast.zig index a0e7754896..cab7749691 100644 --- a/zig/lib/std/zig/ast.zig +++ b/zig/lib/std/zig/ast.zig @@ -1979,20 +1979,26 @@ pub const Tree = struct { // asm ("foo" :: [_] "" (y) : "a", "b"); const last_input = result.inputs[result.inputs.len - 1]; const rparen = tree.lastToken(last_input); - if (token_tags[rparen + 1] == .colon and - token_tags[rparen + 2] == .string_literal) + var i = rparen + 1; + // Allow a (useless) comma right after the closing parenthesis. + if (token_tags[i] == .comma) i += 1; + if (token_tags[i] == .colon and + token_tags[i + 1] == .string_literal) { - result.first_clobber = rparen + 2; + result.first_clobber = i + 1; } } else { // asm ("foo" : [_] "" (x) :: "a", "b"); const last_output = result.outputs[result.outputs.len - 1]; const rparen = tree.lastToken(last_output); - if (token_tags[rparen + 1] == .colon and - token_tags[rparen + 2] == .colon and - token_tags[rparen + 3] == .string_literal) + var i = rparen + 1; + // Allow a (useless) comma right after the closing parenthesis. + if (token_tags[i] == .comma) i += 1; + if (token_tags[i] == .colon and + token_tags[i + 1] == .colon and + token_tags[i + 2] == .string_literal) { - result.first_clobber = rparen + 3; + result.first_clobber = i + 2; } } diff --git a/zig/lib/std/zig/parse.zig b/zig/lib/std/zig/parse.zig index 029d8ede50..d6880ad273 100644 --- a/zig/lib/std/zig/parse.zig +++ b/zig/lib/std/zig/parse.zig @@ -852,7 +852,7 @@ const Parser = struct { /// <- KEYWORD_comptime? VarDecl /// / KEYWORD_comptime BlockExprStatement /// / KEYWORD_nosuspend BlockExprStatement - /// / KEYWORD_suspend (SEMICOLON / BlockExprStatement) + /// / KEYWORD_suspend BlockExprStatement /// / KEYWORD_defer BlockExprStatement /// / KEYWORD_errdefer Payload? BlockExprStatement /// / IfStatement @@ -892,6 +892,7 @@ const Parser = struct { }, .keyword_suspend => { const token = p.nextToken(); + // TODO remove this special case when 0.9.0 is released. const block_expr: Node.Index = if (p.eatToken(.semicolon) != null) 0 else diff --git a/zig/lib/std/zig/parser_test.zig b/zig/lib/std/zig/parser_test.zig index 1bed86f42d..fe119a5d27 100644 --- a/zig/lib/std/zig/parser_test.zig +++ b/zig/lib/std/zig/parser_test.zig @@ -4,6 +4,38 @@ // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. +test "zig fmt: preserves clobbers in inline asm with stray comma" { + try testTransform( + \\fn foo() void { + \\ asm volatile ("" + \\ : [_] "" (-> type), + \\ : + \\ : "clobber" + \\ ); + \\ asm volatile ("" + \\ : + \\ : [_] "" (type), + \\ : "clobber" + \\ ); + \\} + \\ + , + \\fn foo() void { + \\ asm volatile ("" + \\ : [_] "" (-> type) + \\ : + \\ : "clobber" + \\ ); + \\ asm volatile ("" + \\ : + \\ : [_] "" (type) + \\ : "clobber" + \\ ); + \\} + \\ + ); +} + test "zig fmt: respect line breaks in struct field value declaration" { try testCanonical( \\const Foo = struct { @@ -40,6 +72,21 @@ test "zig fmt: rewrite inline functions as callconv(.Inline)" { ); } +// TODO Remove this after zig 0.9.0 is released. +test "zig fmt: rewrite suspend without block expression" { + try testTransform( + \\fn foo() void { + \\ suspend; + \\} + \\ + , + \\fn foo() void { + \\ suspend {} + \\} + \\ + ); +} + test "zig fmt: simple top level comptime block" { try testCanonical( \\// line comment @@ -1315,6 +1362,27 @@ test "zig fmt: 'zig fmt: (off|on)' works in the middle of code" { ); } +test "zig fmt: 'zig fmt: on' indentation is unchanged" { + try testCanonical( + \\fn initOptionsAndLayouts(output: *Output, context: *Context) !void { + \\ // zig fmt: off + \\ try output.main_amount.init(output, "main_amount"); errdefer optput.main_amount.deinit(); + \\ try output.main_factor.init(output, "main_factor"); errdefer optput.main_factor.deinit(); + \\ try output.view_padding.init(output, "view_padding"); errdefer optput.view_padding.deinit(); + \\ try output.outer_padding.init(output, "outer_padding"); errdefer optput.outer_padding.deinit(); + \\ // zig fmt: on + \\ + \\ // zig fmt: off + \\ try output.top.init(output, .top); errdefer optput.top.deinit(); + \\ try output.right.init(output, .right); errdefer optput.right.deinit(); + \\ try output.bottom.init(output, .bottom); errdefer optput.bottom.deinit(); + \\ try output.left.init(output, .left); errdefer optput.left.deinit(); + \\ // zig fmt: on + \\} + \\ + ); +} + test "zig fmt: pointer of unknown length" { try testCanonical( \\fn foo(ptr: [*]u8) void {} @@ -3644,9 +3712,9 @@ test "zig fmt: async functions" { \\fn simpleAsyncFn() void { \\ const a = async a.b(); \\ x += 1; - \\ suspend; + \\ suspend {} \\ x += 1; - \\ suspend; + \\ suspend {} \\ const p: anyframe->void = async simpleAsyncFn() catch unreachable; \\ await p; \\} @@ -5001,6 +5069,21 @@ test "recovery: invalid comptime" { }); } +test "recovery: missing block after suspend" { + // TODO Enable this after zig 0.9.0 is released. + if (true) return error.SkipZigTest; + + try testError( + \\fn foo() void { + \\ suspend; + \\ nosuspend; + \\} + , &[_]Error{ + .expected_block_or_expr, + .expected_block_or_expr, + }); +} + test "recovery: missing block after for/while loops" { try testError( \\test "" { while (foo) } @@ -5144,7 +5227,7 @@ fn testError(source: []const u8, expected_errors: []const Error) !void { var tree = try std.zig.parse(std.testing.allocator, source); defer tree.deinit(std.testing.allocator); - std.testing.expect(tree.errors.len == expected_errors.len); + std.testing.expectEqual(expected_errors.len, tree.errors.len); for (expected_errors) |expected, i| { std.testing.expectEqual(expected, tree.errors[i].tag); } diff --git a/zig/lib/std/zig/render.zig b/zig/lib/std/zig/render.zig index 71d5e51bb9..a762b78728 100644 --- a/zig/lib/std/zig/render.zig +++ b/zig/lib/std/zig/render.zig @@ -269,7 +269,12 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.I try renderToken(ais, tree, suspend_token, .space); return renderExpression(gpa, ais, tree, body, space); } else { - return renderToken(ais, tree, suspend_token, space); + // TODO remove this special case when 0.9.0 is released. + assert(space == .semicolon); + try renderToken(ais, tree, suspend_token, .space); + try ais.writer().writeAll("{}"); + try ais.insertNewline(); + return; } }, @@ -2311,9 +2316,9 @@ fn renderComments(ais: *Ais, tree: ast.Tree, start: usize, end: usize) Error!boo // to the underlying writer, fixing up invaild whitespace. const disabled_source = tree.source[ais.disabled_offset.?..comment_start]; try writeFixingWhitespace(ais.underlying_writer, disabled_source); - ais.disabled_offset = null; // Write with the canonical single space. - try ais.writer().writeAll("// zig fmt: on\n"); + try ais.underlying_writer.writeAll("// zig fmt: on\n"); + ais.disabled_offset = null; } else if (ais.disabled_offset == null and mem.eql(u8, comment_content, "zig fmt: off")) { // Write with the canonical single space. try ais.writer().writeAll("// zig fmt: off\n"); diff --git a/zig/lib/std/zig/system.zig b/zig/lib/std/zig/system.zig index 485bd479bf..42099c6efe 100644 --- a/zig/lib/std/zig/system.zig +++ b/zig/lib/std/zig/system.zig @@ -14,6 +14,7 @@ const process = std.process; const Target = std.Target; const CrossTarget = std.zig.CrossTarget; const macos = @import("system/macos.zig"); +const linux = @import("system/linux.zig"); pub const windows = @import("system/windows.zig"); pub const getSDKPath = macos.getSDKPath; @@ -911,15 +912,19 @@ pub const NativeTargetInfo = struct { .x86_64, .i386 => { return @import("system/x86.zig").detectNativeCpuAndFeatures(cpu_arch, os, cross_target); }, - else => { - // This architecture does not have CPU model & feature detection yet. - // See https://github.com/ziglang/zig/issues/4591 - return null; - }, + else => {}, } + + // This architecture does not have CPU model & feature detection yet. + // See https://github.com/ziglang/zig/issues/4591 + if (std.Target.current.os.tag != .linux) + return null; + + return linux.detectNativeCpuAndFeatures(); } }; test { _ = @import("system/macos.zig"); + _ = @import("system/linux.zig"); } diff --git a/zig/lib/std/zig/system/linux.zig b/zig/lib/std/zig/system/linux.zig new file mode 100644 index 0000000000..c2cf7b009d --- /dev/null +++ b/zig/lib/std/zig/system/linux.zig @@ -0,0 +1,468 @@ +const std = @import("std"); +const mem = std.mem; +const io = std.io; +const fs = std.fs; +const fmt = std.fmt; +const testing = std.testing; + +const Target = std.Target; +const CrossTarget = std.zig.CrossTarget; + +const assert = std.debug.assert; + +const SparcCpuinfoImpl = struct { + model: ?*const Target.Cpu.Model = null, + is_64bit: bool = false, + + const cpu_names = .{ + .{ "SuperSparc", &Target.sparc.cpu.supersparc }, + .{ "HyperSparc", &Target.sparc.cpu.hypersparc }, + .{ "SpitFire", &Target.sparc.cpu.ultrasparc }, + .{ "BlackBird", &Target.sparc.cpu.ultrasparc }, + .{ "Sabre", &Target.sparc.cpu.ultrasparc }, + .{ "Hummingbird", &Target.sparc.cpu.ultrasparc }, + .{ "Cheetah", &Target.sparc.cpu.ultrasparc3 }, + .{ "Jalapeno", &Target.sparc.cpu.ultrasparc3 }, + .{ "Jaguar", &Target.sparc.cpu.ultrasparc3 }, + .{ "Panther", &Target.sparc.cpu.ultrasparc3 }, + .{ "Serrano", &Target.sparc.cpu.ultrasparc3 }, + .{ "UltraSparc T1", &Target.sparc.cpu.niagara }, + .{ "UltraSparc T2", &Target.sparc.cpu.niagara2 }, + .{ "UltraSparc T3", &Target.sparc.cpu.niagara3 }, + .{ "UltraSparc T4", &Target.sparc.cpu.niagara4 }, + .{ "UltraSparc T5", &Target.sparc.cpu.niagara4 }, + .{ "LEON", &Target.sparc.cpu.leon3 }, + }; + + fn line_hook(self: *SparcCpuinfoImpl, key: []const u8, value: []const u8) !bool { + if (mem.eql(u8, key, "cpu")) { + inline for (cpu_names) |pair| { + if (mem.indexOfPos(u8, value, 0, pair[0]) != null) { + self.model = pair[1]; + break; + } + } + } else if (mem.eql(u8, key, "type")) { + self.is_64bit = mem.eql(u8, value, "sun4u") or mem.eql(u8, value, "sun4v"); + } + + return true; + } + + fn finalize(self: *const SparcCpuinfoImpl, arch: Target.Cpu.Arch) ?Target.Cpu { + // At the moment we only support 64bit SPARC systems. + assert(self.is_64bit); + + const model = self.model orelse return null; + return Target.Cpu{ + .arch = arch, + .model = model, + .features = model.features, + }; + } +}; + +const SparcCpuinfoParser = CpuinfoParser(SparcCpuinfoImpl); + +test "cpuinfo: SPARC" { + try testParser(SparcCpuinfoParser, .sparcv9, &Target.sparc.cpu.niagara2, + \\cpu : UltraSparc T2 (Niagara2) + \\fpu : UltraSparc T2 integrated FPU + \\pmu : niagara2 + \\type : sun4v + ); +} + +const PowerpcCpuinfoImpl = struct { + model: ?*const Target.Cpu.Model = null, + + const cpu_names = .{ + .{ "604e", &Target.powerpc.cpu.@"604e" }, + .{ "604", &Target.powerpc.cpu.@"604" }, + .{ "7400", &Target.powerpc.cpu.@"7400" }, + .{ "7410", &Target.powerpc.cpu.@"7400" }, + .{ "7447", &Target.powerpc.cpu.@"7400" }, + .{ "7455", &Target.powerpc.cpu.@"7450" }, + .{ "G4", &Target.powerpc.cpu.@"g4" }, + .{ "POWER4", &Target.powerpc.cpu.@"970" }, + .{ "PPC970FX", &Target.powerpc.cpu.@"970" }, + .{ "PPC970MP", &Target.powerpc.cpu.@"970" }, + .{ "G5", &Target.powerpc.cpu.@"g5" }, + .{ "POWER5", &Target.powerpc.cpu.@"g5" }, + .{ "A2", &Target.powerpc.cpu.@"a2" }, + .{ "POWER6", &Target.powerpc.cpu.@"pwr6" }, + .{ "POWER7", &Target.powerpc.cpu.@"pwr7" }, + .{ "POWER8", &Target.powerpc.cpu.@"pwr8" }, + .{ "POWER8E", &Target.powerpc.cpu.@"pwr8" }, + .{ "POWER8NVL", &Target.powerpc.cpu.@"pwr8" }, + .{ "POWER9", &Target.powerpc.cpu.@"pwr9" }, + .{ "POWER10", &Target.powerpc.cpu.@"pwr10" }, + }; + + fn line_hook(self: *PowerpcCpuinfoImpl, key: []const u8, value: []const u8) !bool { + if (mem.eql(u8, key, "cpu")) { + // The model name is often followed by a comma or space and extra + // info. + inline for (cpu_names) |pair| { + const end_index = mem.indexOfAny(u8, value, ", ") orelse value.len; + if (mem.eql(u8, value[0..end_index], pair[0])) { + self.model = pair[1]; + break; + } + } + + // Stop the detection once we've seen the first core. + return false; + } + + return true; + } + + fn finalize(self: *const PowerpcCpuinfoImpl, arch: Target.Cpu.Arch) ?Target.Cpu { + const model = self.model orelse return null; + return Target.Cpu{ + .arch = arch, + .model = model, + .features = model.features, + }; + } +}; + +const PowerpcCpuinfoParser = CpuinfoParser(PowerpcCpuinfoImpl); + +test "cpuinfo: PowerPC" { + try testParser(PowerpcCpuinfoParser, .powerpc, &Target.powerpc.cpu.@"970", + \\processor : 0 + \\cpu : PPC970MP, altivec supported + \\clock : 1250.000000MHz + \\revision : 1.1 (pvr 0044 0101) + ); + try testParser(PowerpcCpuinfoParser, .powerpc64le, &Target.powerpc.cpu.pwr8, + \\processor : 0 + \\cpu : POWER8 (raw), altivec supported + \\clock : 2926.000000MHz + \\revision : 2.0 (pvr 004d 0200) + ); +} + +const ArmCpuinfoImpl = struct { + cores: [4]CoreInfo = undefined, + core_no: usize = 0, + have_fields: usize = 0, + + const CoreInfo = struct { + architecture: u8 = 0, + implementer: u8 = 0, + variant: u8 = 0, + part: u16 = 0, + is_really_v6: bool = false, + }; + + const cpu_models = struct { + // Shorthands to simplify the tables below. + const A32 = Target.arm.cpu; + const A64 = Target.aarch64.cpu; + + const E = struct { + part: u16, + variant: ?u8 = null, // null if matches any variant + m32: ?*const Target.Cpu.Model = null, + m64: ?*const Target.Cpu.Model = null, + }; + + // implementer = 0x41 + const ARM = [_]E{ + E{ .part = 0x926, .m32 = &A32.arm926ej_s, .m64 = null }, + E{ .part = 0xb02, .m32 = &A32.mpcore, .m64 = null }, + E{ .part = 0xb36, .m32 = &A32.arm1136j_s, .m64 = null }, + E{ .part = 0xb56, .m32 = &A32.arm1156t2_s, .m64 = null }, + E{ .part = 0xb76, .m32 = &A32.arm1176jz_s, .m64 = null }, + E{ .part = 0xc05, .m32 = &A32.cortex_a5, .m64 = null }, + E{ .part = 0xc07, .m32 = &A32.cortex_a7, .m64 = null }, + E{ .part = 0xc08, .m32 = &A32.cortex_a8, .m64 = null }, + E{ .part = 0xc09, .m32 = &A32.cortex_a9, .m64 = null }, + E{ .part = 0xc0d, .m32 = &A32.cortex_a17, .m64 = null }, + E{ .part = 0xc0f, .m32 = &A32.cortex_a15, .m64 = null }, + E{ .part = 0xc0e, .m32 = &A32.cortex_a17, .m64 = null }, + E{ .part = 0xc14, .m32 = &A32.cortex_r4, .m64 = null }, + E{ .part = 0xc15, .m32 = &A32.cortex_r5, .m64 = null }, + E{ .part = 0xc17, .m32 = &A32.cortex_r7, .m64 = null }, + E{ .part = 0xc18, .m32 = &A32.cortex_r8, .m64 = null }, + E{ .part = 0xc20, .m32 = &A32.cortex_m0, .m64 = null }, + E{ .part = 0xc21, .m32 = &A32.cortex_m1, .m64 = null }, + E{ .part = 0xc23, .m32 = &A32.cortex_m3, .m64 = null }, + E{ .part = 0xc24, .m32 = &A32.cortex_m4, .m64 = null }, + E{ .part = 0xc27, .m32 = &A32.cortex_m7, .m64 = null }, + E{ .part = 0xc60, .m32 = &A32.cortex_m0plus, .m64 = null }, + E{ .part = 0xd01, .m32 = &A32.cortex_a32, .m64 = null }, + E{ .part = 0xd03, .m32 = &A32.cortex_a53, .m64 = &A64.cortex_a53 }, + E{ .part = 0xd04, .m32 = &A32.cortex_a35, .m64 = &A64.cortex_a35 }, + E{ .part = 0xd05, .m32 = &A32.cortex_a55, .m64 = &A64.cortex_a55 }, + E{ .part = 0xd07, .m32 = &A32.cortex_a57, .m64 = &A64.cortex_a57 }, + E{ .part = 0xd08, .m32 = &A32.cortex_a72, .m64 = &A64.cortex_a72 }, + E{ .part = 0xd09, .m32 = &A32.cortex_a73, .m64 = &A64.cortex_a73 }, + E{ .part = 0xd0a, .m32 = &A32.cortex_a75, .m64 = &A64.cortex_a75 }, + E{ .part = 0xd0b, .m32 = &A32.cortex_a76, .m64 = &A64.cortex_a76 }, + E{ .part = 0xd0c, .m32 = &A32.neoverse_n1, .m64 = null }, + E{ .part = 0xd0d, .m32 = &A32.cortex_a77, .m64 = &A64.cortex_a77 }, + E{ .part = 0xd13, .m32 = &A32.cortex_r52, .m64 = null }, + E{ .part = 0xd20, .m32 = &A32.cortex_m23, .m64 = null }, + E{ .part = 0xd21, .m32 = &A32.cortex_m33, .m64 = null }, + E{ .part = 0xd41, .m32 = &A32.cortex_a78, .m64 = &A64.cortex_a78 }, + E{ .part = 0xd4b, .m32 = &A32.cortex_a78c, .m64 = &A64.cortex_a78c }, + E{ .part = 0xd44, .m32 = &A32.cortex_x1, .m64 = &A64.cortex_x1 }, + E{ .part = 0xd02, .m64 = &A64.cortex_a34 }, + E{ .part = 0xd06, .m64 = &A64.cortex_a65 }, + E{ .part = 0xd43, .m64 = &A64.cortex_a65ae }, + }; + // implementer = 0x42 + const Broadcom = [_]E{ + E{ .part = 0x516, .m64 = &A64.thunderx2t99 }, + }; + // implementer = 0x43 + const Cavium = [_]E{ + E{ .part = 0x0a0, .m64 = &A64.thunderx }, + E{ .part = 0x0a2, .m64 = &A64.thunderxt81 }, + E{ .part = 0x0a3, .m64 = &A64.thunderxt83 }, + E{ .part = 0x0a1, .m64 = &A64.thunderxt88 }, + E{ .part = 0x0af, .m64 = &A64.thunderx2t99 }, + }; + // implementer = 0x46 + const Fujitsu = [_]E{ + E{ .part = 0x001, .m64 = &A64.a64fx }, + }; + // implementer = 0x48 + const HiSilicon = [_]E{ + E{ .part = 0xd01, .m64 = &A64.tsv110 }, + }; + // implementer = 0x4e + const Nvidia = [_]E{ + E{ .part = 0x004, .m64 = &A64.carmel }, + }; + // implementer = 0x50 + const Ampere = [_]E{ + E{ .part = 0x000, .variant = 3, .m64 = &A64.emag }, + E{ .part = 0x000, .m64 = &A64.xgene1 }, + }; + // implementer = 0x51 + const Qualcomm = [_]E{ + E{ .part = 0x06f, .m32 = &A32.krait }, + E{ .part = 0x201, .m64 = &A64.kryo, .m32 = &A64.kryo }, + E{ .part = 0x205, .m64 = &A64.kryo, .m32 = &A64.kryo }, + E{ .part = 0x211, .m64 = &A64.kryo, .m32 = &A64.kryo }, + E{ .part = 0x800, .m64 = &A64.cortex_a73, .m32 = &A64.cortex_a73 }, + E{ .part = 0x801, .m64 = &A64.cortex_a73, .m32 = &A64.cortex_a73 }, + E{ .part = 0x802, .m64 = &A64.cortex_a75, .m32 = &A64.cortex_a75 }, + E{ .part = 0x803, .m64 = &A64.cortex_a75, .m32 = &A64.cortex_a75 }, + E{ .part = 0x804, .m64 = &A64.cortex_a76, .m32 = &A64.cortex_a76 }, + E{ .part = 0x805, .m64 = &A64.cortex_a76, .m32 = &A64.cortex_a76 }, + E{ .part = 0xc00, .m64 = &A64.falkor }, + E{ .part = 0xc01, .m64 = &A64.saphira }, + }; + + fn isKnown(core: CoreInfo, is_64bit: bool) ?*const Target.Cpu.Model { + const models = switch (core.implementer) { + 0x41 => &ARM, + 0x42 => &Broadcom, + 0x43 => &Cavium, + 0x46 => &Fujitsu, + 0x48 => &HiSilicon, + 0x50 => &Ampere, + 0x51 => &Qualcomm, + else => return null, + }; + + for (models) |model| { + if (model.part == core.part and + (model.variant == null or model.variant.? == core.variant)) + return if (is_64bit) model.m64 else model.m32; + } + + return null; + } + }; + + fn addOne(self: *ArmCpuinfoImpl) void { + if (self.have_fields == 4 and self.core_no < self.cores.len) { + if (self.core_no > 0) { + // Deduplicate the core info. + for (self.cores[0..self.core_no]) |it| { + if (std.meta.eql(it, self.cores[self.core_no])) + return; + } + } + self.core_no += 1; + } + } + + fn line_hook(self: *ArmCpuinfoImpl, key: []const u8, value: []const u8) !bool { + const info = &self.cores[self.core_no]; + + if (mem.eql(u8, key, "processor")) { + // Handle both old-style and new-style cpuinfo formats. + // The former prints a sequence of "processor: N" lines for each + // core and then the info for the core that's executing this code(!) + // while the latter prints the infos for each core right after the + // "processor" key. + self.have_fields = 0; + self.cores[self.core_no] = .{}; + } else if (mem.eql(u8, key, "CPU implementer")) { + info.implementer = try fmt.parseInt(u8, value, 0); + self.have_fields += 1; + } else if (mem.eql(u8, key, "CPU architecture")) { + // "AArch64" on older kernels. + info.architecture = if (mem.startsWith(u8, value, "AArch64")) + 8 + else + try fmt.parseInt(u8, value, 0); + self.have_fields += 1; + } else if (mem.eql(u8, key, "CPU variant")) { + info.variant = try fmt.parseInt(u8, value, 0); + self.have_fields += 1; + } else if (mem.eql(u8, key, "CPU part")) { + info.part = try fmt.parseInt(u16, value, 0); + self.have_fields += 1; + } else if (mem.eql(u8, key, "model name")) { + // ARMv6 cores report "CPU architecture" equal to 7. + if (mem.indexOf(u8, value, "(v6l)")) |_| { + info.is_really_v6 = true; + } + } else if (mem.eql(u8, key, "CPU revision")) { + // This field is always the last one for each CPU section. + _ = self.addOne(); + } + + return true; + } + + fn finalize(self: *ArmCpuinfoImpl, arch: Target.Cpu.Arch) ?Target.Cpu { + if (self.core_no == 0) return null; + + const is_64bit = switch (arch) { + .aarch64, .aarch64_be, .aarch64_32 => true, + else => false, + }; + + var known_models: [self.cores.len]?*const Target.Cpu.Model = undefined; + for (self.cores[0..self.core_no]) |core, i| { + known_models[i] = cpu_models.isKnown(core, is_64bit); + } + + // XXX We pick the first core on big.LITTLE systems, hopefully the + // LITTLE one. + const model = known_models[0] orelse return null; + return Target.Cpu{ + .arch = arch, + .model = model, + .features = model.features, + }; + } +}; + +const ArmCpuinfoParser = CpuinfoParser(ArmCpuinfoImpl); + +test "cpuinfo: ARM" { + try testParser(ArmCpuinfoParser, .arm, &Target.arm.cpu.arm1176jz_s, + \\processor : 0 + \\model name : ARMv6-compatible processor rev 7 (v6l) + \\BogoMIPS : 997.08 + \\Features : half thumb fastmult vfp edsp java tls + \\CPU implementer : 0x41 + \\CPU architecture: 7 + \\CPU variant : 0x0 + \\CPU part : 0xb76 + \\CPU revision : 7 + ); + try testParser(ArmCpuinfoParser, .arm, &Target.arm.cpu.cortex_a7, + \\processor : 0 + \\model name : ARMv7 Processor rev 3 (v7l) + \\BogoMIPS : 18.00 + \\Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae + \\CPU implementer : 0x41 + \\CPU architecture: 7 + \\CPU variant : 0x0 + \\CPU part : 0xc07 + \\CPU revision : 3 + \\ + \\processor : 4 + \\model name : ARMv7 Processor rev 3 (v7l) + \\BogoMIPS : 90.00 + \\Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae + \\CPU implementer : 0x41 + \\CPU architecture: 7 + \\CPU variant : 0x2 + \\CPU part : 0xc0f + \\CPU revision : 3 + ); + try testParser(ArmCpuinfoParser, .aarch64, &Target.aarch64.cpu.cortex_a72, + \\processor : 0 + \\BogoMIPS : 108.00 + \\Features : fp asimd evtstrm crc32 cpuid + \\CPU implementer : 0x41 + \\CPU architecture: 8 + \\CPU variant : 0x0 + \\CPU part : 0xd08 + \\CPU revision : 3 + ); +} + +fn testParser( + parser: anytype, + arch: Target.Cpu.Arch, + expected_model: *const Target.Cpu.Model, + input: []const u8, +) !void { + var fbs = io.fixedBufferStream(input); + const result = try parser.parse(arch, fbs.reader()); + testing.expectEqual(expected_model, result.?.model); + testing.expect(expected_model.features.eql(result.?.features)); +} + +// The generic implementation of a /proc/cpuinfo parser. +// For every line it invokes the line_hook method with the key and value strings +// as first and second parameters. Returning false from the hook function stops +// the iteration without raising an error. +// When all the lines have been analyzed the finalize method is called. +fn CpuinfoParser(comptime impl: anytype) type { + return struct { + fn parse(arch: Target.Cpu.Arch, reader: anytype) anyerror!?Target.Cpu { + var line_buf: [1024]u8 = undefined; + var obj: impl = .{}; + + while (true) { + const line = (try reader.readUntilDelimiterOrEof(&line_buf, '\n')) orelse break; + const colon_pos = mem.indexOfScalar(u8, line, ':') orelse continue; + const key = mem.trimRight(u8, line[0..colon_pos], " \t"); + const value = mem.trimLeft(u8, line[colon_pos + 1 ..], " \t"); + + if (!try obj.line_hook(key, value)) + break; + } + + return obj.finalize(arch); + } + }; +} + +pub fn detectNativeCpuAndFeatures() ?Target.Cpu { + var f = fs.openFileAbsolute("/proc/cpuinfo", .{ .intended_io_mode = .blocking }) catch |err| switch (err) { + else => return null, + }; + defer f.close(); + + const current_arch = std.Target.current.cpu.arch; + switch (current_arch) { + .arm, .armeb, .thumb, .thumbeb, .aarch64, .aarch64_be, .aarch64_32 => { + return ArmCpuinfoParser.parse(current_arch, f.reader()) catch null; + }, + .sparcv9 => { + return SparcCpuinfoParser.parse(current_arch, f.reader()) catch null; + }, + .powerpc, .powerpcle, .powerpc64, .powerpc64le => { + return PowerpcCpuinfoParser.parse(current_arch, f.reader()) catch null; + }, + else => {}, + } + + return null; +} diff --git a/zig/src/Compilation.zig b/zig/src/Compilation.zig index 485d7690f5..58d6f41858 100644 --- a/zig/src/Compilation.zig +++ b/zig/src/Compilation.zig @@ -53,6 +53,9 @@ c_object_work_queue: std.fifo.LinearFifo(*CObject, .Dynamic), /// This data is accessed by multiple threads and is protected by `mutex`. failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *CObject.ErrorMsg) = .{}, +/// Miscellaneous things that can fail. +misc_failures: std.AutoArrayHashMapUnmanaged(MiscTask, MiscError) = .{}, + keep_source_files_loaded: bool, use_clang: bool, sanitize_c: bool, @@ -229,7 +232,6 @@ pub const CObject = struct { pub fn destroy(em: *ErrorMsg, gpa: *Allocator) void { gpa.free(em.msg); gpa.destroy(em); - em.* = undefined; } }; @@ -256,6 +258,36 @@ pub const CObject = struct { } }; +pub const MiscTask = enum { + write_builtin_zig, + glibc_crt_file, + glibc_shared_objects, + musl_crt_file, + mingw_crt_file, + windows_import_lib, + libunwind, + libcxx, + libcxxabi, + libtsan, + compiler_rt, + libssp, + zig_libc, +}; + +pub const MiscError = struct { + /// Allocated with gpa. + msg: []u8, + children: ?AllErrors = null, + + pub fn deinit(misc_err: *MiscError, gpa: *Allocator) void { + gpa.free(misc_err.msg); + if (misc_err.children) |*children| { + children.deinit(gpa); + } + misc_err.* = undefined; + } +}; + /// To support incremental compilation, errors are stored in various places /// so that they can be created and destroyed appropriately. This structure /// is used to collect all the errors from the various places into one @@ -278,6 +310,7 @@ pub const AllErrors = struct { }, plain: struct { msg: []const u8, + notes: []Message = &.{}, }, pub fn renderToStdErr(msg: Message, ttyconf: std.debug.TTY.Config) void { @@ -285,7 +318,7 @@ pub const AllErrors = struct { const held = std.debug.getStderrMutex().acquire(); defer held.release(); const stderr = std.io.getStdErr(); - return msg.renderToStdErrInner(ttyconf, stderr, "error:", .Red) catch return; + return msg.renderToStdErrInner(ttyconf, stderr, "error:", .Red, 0) catch return; } fn renderToStdErrInner( @@ -294,6 +327,7 @@ pub const AllErrors = struct { stderr_file: std.fs.File, kind: []const u8, color: std.debug.TTY.Color, + indent: usize, ) anyerror!void { const stderr = stderr_file.writer(); switch (msg) { @@ -305,6 +339,7 @@ pub const AllErrors = struct { src.column + 1, }); ttyconf.setColor(stderr, color); + try stderr.writeByteNTimes(' ', indent); try stderr.writeAll(kind); ttyconf.setColor(stderr, .Bold); try stderr.print(" {s}\n", .{src.msg}); @@ -318,11 +353,19 @@ pub const AllErrors = struct { ttyconf.setColor(stderr, .Reset); } for (src.notes) |note| { - try note.renderToStdErrInner(ttyconf, stderr_file, "note:", .Cyan); + try note.renderToStdErrInner(ttyconf, stderr_file, "note:", .Cyan, indent); } }, .plain => |plain| { - try stderr.print("{s}: {s}\n", .{ kind, plain.msg }); + ttyconf.setColor(stderr, color); + try stderr.writeByteNTimes(' ', indent); + try stderr.writeAll(kind); + ttyconf.setColor(stderr, .Reset); + try stderr.print(" {s}\n", .{plain.msg}); + ttyconf.setColor(stderr, .Reset); + for (plain.notes) |note| { + try note.renderToStdErrInner(ttyconf, stderr_file, "error:", .Red, indent + 4); + } }, } } @@ -380,6 +423,45 @@ pub const AllErrors = struct { ) !void { try errors.append(.{ .plain = .{ .msg = msg } }); } + + fn addPlainWithChildren( + arena: *std.heap.ArenaAllocator, + errors: *std.ArrayList(Message), + msg: []const u8, + optional_children: ?AllErrors, + ) !void { + const duped_msg = try arena.allocator.dupe(u8, msg); + if (optional_children) |*children| { + try errors.append(.{ .plain = .{ + .msg = duped_msg, + .notes = try dupeList(children.list, &arena.allocator), + } }); + } else { + try errors.append(.{ .plain = .{ .msg = duped_msg } }); + } + } + + fn dupeList(list: []const Message, arena: *Allocator) Allocator.Error![]Message { + const duped_list = try arena.alloc(Message, list.len); + for (list) |item, i| { + duped_list[i] = switch (item) { + .src => |src| .{ .src = .{ + .msg = try arena.dupe(u8, src.msg), + .src_path = try arena.dupe(u8, src.src_path), + .line = src.line, + .column = src.column, + .byte_offset = src.byte_offset, + .source_line = if (src.source_line) |s| try arena.dupe(u8, s) else null, + .notes = try dupeList(src.notes, arena), + } }, + .plain => |plain| .{ .plain = .{ + .msg = try arena.dupe(u8, plain.msg), + .notes = try dupeList(plain.notes, arena), + } }, + }; + } + return duped_list; + } }; pub const Directory = struct { @@ -1357,6 +1439,8 @@ pub fn destroy(self: *Compilation) void { } self.failed_c_objects.deinit(gpa); + self.clearMiscFailures(); + self.cache_parent.manifest_dir.close(); if (self.owned_link_dir) |*dir| dir.close(); @@ -1366,6 +1450,14 @@ pub fn destroy(self: *Compilation) void { self.arena_state.promote(gpa).deinit(); } +pub fn clearMiscFailures(comp: *Compilation) void { + for (comp.misc_failures.items()) |*entry| { + entry.value.deinit(comp.gpa); + } + comp.misc_failures.deinit(comp.gpa); + comp.misc_failures = .{}; +} + pub fn getTarget(self: Compilation) Target { return self.bin_file.options.target; } @@ -1375,6 +1467,7 @@ pub fn update(self: *Compilation) !void { const tracy = trace(@src()); defer tracy.end(); + self.clearMiscFailures(); self.c_object_cache_digest_set.clearRetainingCapacity(); // For compiling C objects, we rely on the cache hash system to avoid duplicating work. @@ -1475,7 +1568,7 @@ pub fn makeBinFileWritable(self: *Compilation) !void { } pub fn totalErrorCount(self: *Compilation) usize { - var total: usize = self.failed_c_objects.items().len; + var total: usize = self.failed_c_objects.count() + self.misc_failures.count(); if (self.bin_file.options.module) |module| { total += module.failed_exports.items().len + @@ -1539,6 +1632,9 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { }, }); } + for (self.misc_failures.items()) |entry| { + try AllErrors.addPlainWithChildren(&arena, &errors, entry.value.msg, entry.value.children); + } if (self.bin_file.options.module) |module| { for (module.failed_files.items()) |entry| { try AllErrors.add(module, &arena, &errors, entry.value.*); @@ -1775,89 +1871,160 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor }, .glibc_crt_file => |crt_file| { glibc.buildCRTFile(self, crt_file) catch |err| { - // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build glibc CRT file: {s}", .{@errorName(err)}); + // TODO Surface more error details. + try self.setMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{ + @errorName(err), + }); }; }, .glibc_shared_objects => { glibc.buildSharedObjects(self) catch |err| { - // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build glibc shared objects: {s}", .{@errorName(err)}); + // TODO Surface more error details. + try self.setMiscFailure( + .glibc_shared_objects, + "unable to build glibc shared objects: {s}", + .{@errorName(err)}, + ); }; }, .musl_crt_file => |crt_file| { musl.buildCRTFile(self, crt_file) catch |err| { - // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build musl CRT file: {s}", .{@errorName(err)}); + // TODO Surface more error details. + try self.setMiscFailure( + .musl_crt_file, + "unable to build musl CRT file: {s}", + .{@errorName(err)}, + ); }; }, .mingw_crt_file => |crt_file| { mingw.buildCRTFile(self, crt_file) catch |err| { - // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build mingw-w64 CRT file: {s}", .{@errorName(err)}); + // TODO Surface more error details. + try self.setMiscFailure( + .mingw_crt_file, + "unable to build mingw-w64 CRT file: {s}", + .{@errorName(err)}, + ); }; }, .windows_import_lib => |index| { const link_lib = self.bin_file.options.system_libs.items()[index].key; mingw.buildImportLib(self, link_lib) catch |err| { - // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to generate DLL import .lib file: {s}", .{@errorName(err)}); + // TODO Surface more error details. + try self.setMiscFailure( + .windows_import_lib, + "unable to generate DLL import .lib file: {s}", + .{@errorName(err)}, + ); }; }, .libunwind => { libunwind.buildStaticLib(self) catch |err| { - // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build libunwind: {s}", .{@errorName(err)}); + // TODO Surface more error details. + try self.setMiscFailure( + .libunwind, + "unable to build libunwind: {s}", + .{@errorName(err)}, + ); }; }, .libcxx => { libcxx.buildLibCXX(self) catch |err| { - // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build libcxx: {s}", .{@errorName(err)}); + // TODO Surface more error details. + try self.setMiscFailure( + .libcxx, + "unable to build libcxx: {s}", + .{@errorName(err)}, + ); }; }, .libcxxabi => { libcxx.buildLibCXXABI(self) catch |err| { - // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build libcxxabi: {s}", .{@errorName(err)}); + // TODO Surface more error details. + try self.setMiscFailure( + .libcxxabi, + "unable to build libcxxabi: {s}", + .{@errorName(err)}, + ); }; }, .libtsan => { libtsan.buildTsan(self) catch |err| { - // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build TSAN library: {s}", .{@errorName(err)}); + // TODO Surface more error details. + try self.setMiscFailure( + .libtsan, + "unable to build TSAN library: {s}", + .{@errorName(err)}, + ); }; }, .compiler_rt_lib => { - self.buildOutputFromZig("compiler_rt.zig", .Lib, &self.compiler_rt_static_lib) catch |err| { - // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build compiler_rt: {s}", .{@errorName(err)}); + self.buildOutputFromZig( + "compiler_rt.zig", + .Lib, + &self.compiler_rt_static_lib, + .compiler_rt, + ) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.SubCompilationFailed => continue, // error reported already + else => try self.setMiscFailure( + .compiler_rt, + "unable to build compiler_rt: {s}", + .{@errorName(err)}, + ), }; }, .compiler_rt_obj => { - self.buildOutputFromZig("compiler_rt.zig", .Obj, &self.compiler_rt_obj) catch |err| { - // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build compiler_rt: {s}", .{@errorName(err)}); + self.buildOutputFromZig( + "compiler_rt.zig", + .Obj, + &self.compiler_rt_obj, + .compiler_rt, + ) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.SubCompilationFailed => continue, // error reported already + else => try self.setMiscFailure( + .compiler_rt, + "unable to build compiler_rt: {s}", + .{@errorName(err)}, + ), }; }, .libssp => { - self.buildOutputFromZig("ssp.zig", .Lib, &self.libssp_static_lib) catch |err| { - // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build libssp: {s}", .{@errorName(err)}); + self.buildOutputFromZig( + "ssp.zig", + .Lib, + &self.libssp_static_lib, + .libssp, + ) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.SubCompilationFailed => continue, // error reported already + else => try self.setMiscFailure( + .libssp, + "unable to build libssp: {s}", + .{@errorName(err)}, + ), }; }, .zig_libc => { - self.buildOutputFromZig("c.zig", .Lib, &self.libc_static_lib) catch |err| { - // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build zig's multitarget libc: {s}", .{@errorName(err)}); + self.buildOutputFromZig( + "c.zig", + .Lib, + &self.libc_static_lib, + .zig_libc, + ) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.SubCompilationFailed => continue, // error reported already + else => try self.setMiscFailure( + .zig_libc, + "unable to build zig's multitarget libc: {s}", + .{@errorName(err)}, + ), }; }, .generate_builtin_zig => { // This Job is only queued up if there is a zig module. - self.updateBuiltinZigFile(self.bin_file.options.module.?) catch |err| { - // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to update builtin.zig file: {s}", .{@errorName(err)}); - }; + try self.updateBuiltinZigFile(self.bin_file.options.module.?); }, .stage1_module => { if (!build_options.is_stage1) @@ -2511,25 +2678,29 @@ pub fn addCCArgs( try argv.append("-fPIC"); } }, - .shared_library, .assembly, .ll, .bc, .unknown, .static_library, .object, .zig => {}, + .shared_library, .ll, .bc, .unknown, .static_library, .object, .zig => {}, + .assembly => { + // Argh, why doesn't the assembler accept the list of CPU features?! + // I don't see a way to do this other than hard coding everything. + switch (target.cpu.arch) { + .riscv32, .riscv64 => { + if (std.Target.riscv.featureSetHas(target.cpu.features, .relax)) { + try argv.append("-mrelax"); + } else { + try argv.append("-mno-relax"); + } + }, + else => { + // TODO + }, + } + if (target.cpu.model.llvm_name) |ln| + try argv.append(try std.fmt.allocPrint(arena, "-mcpu={s}", .{ln})); + }, } if (out_dep_path) |p| { try argv.appendSlice(&[_][]const u8{ "-MD", "-MV", "-MF", p }); } - // Argh, why doesn't the assembler accept the list of CPU features?! - // I don't see a way to do this other than hard coding everything. - switch (target.cpu.arch) { - .riscv32, .riscv64 => { - if (std.Target.riscv.featureSetHas(target.cpu.features, .relax)) { - try argv.append("-mrelax"); - } else { - try argv.append("-mno-relax"); - } - }, - else => { - // TODO - }, - } if (target.os.tag == .freestanding) { try argv.append("-ffreestanding"); @@ -2858,13 +3029,31 @@ fn wantBuildLibUnwindFromSource(comp: *Compilation) bool { target_util.libcNeedsLibUnwind(comp.getTarget()); } -fn updateBuiltinZigFile(comp: *Compilation, mod: *Module) !void { +fn updateBuiltinZigFile(comp: *Compilation, mod: *Module) Allocator.Error!void { const tracy = trace(@src()); defer tracy.end(); const source = try comp.generateBuiltinZigSource(comp.gpa); defer comp.gpa.free(source); - try mod.zig_cache_artifact_directory.handle.writeFile("builtin.zig", source); + + mod.zig_cache_artifact_directory.handle.writeFile("builtin.zig", source) catch |err| { + const dir_path: []const u8 = mod.zig_cache_artifact_directory.path orelse "."; + try comp.setMiscFailure(.write_builtin_zig, "unable to write builtin.zig to {s}: {s}", .{ + dir_path, + @errorName(err), + }); + }; +} + +fn setMiscFailure( + comp: *Compilation, + tag: MiscTask, + comptime format: []const u8, + args: anytype, +) Allocator.Error!void { + try comp.misc_failures.ensureCapacity(comp.gpa, comp.misc_failures.count() + 1); + const msg = try std.fmt.allocPrint(comp.gpa, format, args); + comp.misc_failures.putAssumeCapacityNoClobber(tag, .{ .msg = msg }); } pub fn dump_argv(argv: []const []const u8) void { @@ -2874,7 +3063,7 @@ pub fn dump_argv(argv: []const []const u8) void { std.debug.print("{s}\n", .{argv[argv.len - 1]}); } -pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 { +pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Allocator.Error![]u8 { const tracy = trace(@src()); defer tracy.end(); @@ -3071,6 +3260,10 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void { try sub_compilation.update(); // Look for compilation errors in this sub_compilation + // TODO instead of logging these errors, handle them in the callsites + // of updateSubCompilation and attach them as sub-errors, properly + // surfacing the errors. You can see an example of this already + // done inside buildOutputFromZig. var errors = try sub_compilation.getAllErrorsAlloc(); defer errors.deinit(sub_compilation.gpa); @@ -3099,6 +3292,7 @@ fn buildOutputFromZig( src_basename: []const u8, output_mode: std.builtin.OutputMode, out: *?CRTFile, + misc_task_tag: MiscTask, ) !void { const tracy = trace(@src()); defer tracy.end(); @@ -3173,7 +3367,23 @@ fn buildOutputFromZig( }); defer sub_compilation.destroy(); - try sub_compilation.updateSubCompilation(); + try sub_compilation.update(); + // Look for compilation errors in this sub_compilation. + var keep_errors = false; + var errors = try sub_compilation.getAllErrorsAlloc(); + defer if (!keep_errors) errors.deinit(sub_compilation.gpa); + + if (errors.list.len != 0) { + try comp.misc_failures.ensureCapacity(comp.gpa, comp.misc_failures.count() + 1); + comp.misc_failures.putAssumeCapacityNoClobber(misc_task_tag, .{ + .msg = try std.fmt.allocPrint(comp.gpa, "sub-compilation of {s} failed", .{ + @tagName(misc_task_tag), + }), + .children = errors, + }); + keep_errors = true; + return error.SubCompilationFailed; + } assert(out.* == null); out.* = Compilation.CRTFile{ diff --git a/zig/src/clang.zig b/zig/src/clang.zig index d53accbaf7..e00271968b 100644 --- a/zig/src/clang.zig +++ b/zig/src/clang.zig @@ -104,6 +104,16 @@ pub const APFloat = opaque { extern fn ZigClangAPFloat_toString(*const APFloat, precision: c_uint, maxPadding: c_uint, truncateZero: bool) [*:0]const u8; }; +pub const APFloatBaseSemantics = extern enum { + IEEEhalf, + BFloat, + IEEEsingle, + IEEEdouble, + x86DoubleExtended, + IEEEquad, + PPCDoubleDouble, +}; + pub const APInt = opaque { pub const getLimitedValue = ZigClangAPInt_getLimitedValue; extern fn ZigClangAPInt_getLimitedValue(*const APInt, limit: u64) u64; @@ -455,6 +465,12 @@ pub const FileID = opaque {}; pub const FloatingLiteral = opaque { pub const getValueAsApproximateDouble = ZigClangFloatingLiteral_getValueAsApproximateDouble; extern fn ZigClangFloatingLiteral_getValueAsApproximateDouble(*const FloatingLiteral) f64; + + pub const getBeginLoc = ZigClangIntegerLiteral_getBeginLoc; + extern fn ZigClangIntegerLiteral_getBeginLoc(*const FloatingLiteral) SourceLocation; + + pub const getRawSemantics = ZigClangFloatingLiteral_getRawSemantics; + extern fn ZigClangFloatingLiteral_getRawSemantics(*const FloatingLiteral) APFloatBaseSemantics; }; pub const ForStmt = opaque { diff --git a/zig/src/codegen.zig b/zig/src/codegen.zig index a345fd8058..2f83bfe6f3 100644 --- a/zig/src/codegen.zig +++ b/zig/src/codegen.zig @@ -449,7 +449,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .rbrace_src = src_data.rbrace_src, .source = src_data.source, }; - defer function.register_manager.deinit(bin_file.allocator); defer function.stack.deinit(bin_file.allocator); defer function.exitlude_jump_relocs.deinit(bin_file.allocator); @@ -779,8 +778,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { branch.inst_table.putAssumeCapacity(inst, .dead); switch (prev_value) { .register => |reg| { - const canon_reg = toCanonicalReg(reg); - self.register_manager.freeReg(canon_reg); + // TODO separate architectures with registers from + // stack-based architectures (spu_2) + if (callee_preserved_regs.len > 0) { + const canon_reg = toCanonicalReg(reg); + self.register_manager.freeReg(canon_reg); + } }, else => {}, // TODO process stack allocation death } @@ -920,9 +923,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const ptr_bits = arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); if (abi_size <= ptr_bytes) { - try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1); - if (self.register_manager.tryAllocReg(inst)) |reg| { - return MCValue{ .register = registerAlias(reg, abi_size) }; + // TODO separate architectures with registers from + // stack-based architectures (spu_2) + if (callee_preserved_regs.len > 0) { + if (self.register_manager.tryAllocReg(inst)) |reg| { + return MCValue{ .register = registerAlias(reg, abi_size) }; + } } } } @@ -952,8 +958,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { /// `reg_owner` is the instruction that gets associated with the register in the register table. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue { - try self.register_manager.registers.ensureCapacity(self.gpa, @intCast(u32, self.register_manager.registers.count() + 1)); - const reg = try self.register_manager.allocReg(reg_owner); try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv); return MCValue{ .register = reg }; @@ -1240,14 +1244,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .register => |reg| { // If it's in the registers table, need to associate the register with the // new instruction. - if (self.register_manager.registers.getEntry(toCanonicalReg(reg))) |entry| { - entry.value = inst; + // TODO separate architectures with registers from + // stack-based architectures (spu_2) + if (callee_preserved_regs.len > 0) { + if (reg.allocIndex()) |index| { + if (!self.register_manager.isRegFree(reg)) { + self.register_manager.registers[index] = inst; + } + } + log.debug("reusing {} => {*}", .{ reg, inst }); } - log.debug("reusing {} => {*}", .{ reg, inst }); }, .stack_offset => |off| { log.debug("reusing stack offset {} => {*}", .{ off, inst }); - return true; }, else => return false, } @@ -1739,6 +1748,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const arg_index = self.arg_index; self.arg_index += 1; + // TODO separate architectures with registers from + // stack-based architectures (spu_2) if (callee_preserved_regs.len == 0) { return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch}); } @@ -1770,7 +1781,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { switch (mcv) { .register => |reg| { - try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1); self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base); }, else => {}, @@ -2076,7 +2086,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { switch (mc_arg) { .none => continue, .register => |reg| { - try self.register_manager.getRegWithoutTracking(reg); + // TODO prevent this macho if block to be generated for all archs + switch (arch) { + .x86_64, .aarch64 => try self.register_manager.getRegWithoutTracking(reg), + else => unreachable, + } try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); }, .stack_offset => { @@ -2398,8 +2412,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const parent_free_registers = self.register_manager.free_registers; var parent_stack = try self.stack.clone(self.gpa); defer parent_stack.deinit(self.gpa); - var parent_registers = try self.register_manager.registers.clone(self.gpa); - defer parent_registers.deinit(self.gpa); + const parent_registers = self.register_manager.registers; try self.branch_stack.append(.{}); @@ -2415,9 +2428,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { var saved_then_branch = self.branch_stack.pop(); defer saved_then_branch.deinit(self.gpa); - self.register_manager.registers.deinit(self.gpa); self.register_manager.registers = parent_registers; - parent_registers = .{}; self.stack.deinit(self.gpa); self.stack = parent_stack; diff --git a/zig/src/codegen/riscv64.zig b/zig/src/codegen/riscv64.zig index 96b9c58f9c..a01f38289a 100644 --- a/zig/src/codegen/riscv64.zig +++ b/zig/src/codegen/riscv64.zig @@ -1,5 +1,7 @@ const std = @import("std"); const DW = std.dwarf; +const assert = std.debug.assert; +const testing = std.testing; // TODO: this is only tagged to facilitate the monstrosity. // Once packed structs work make it packed. @@ -110,7 +112,7 @@ pub const Instruction = union(enum) { // -- less burden on callsite, bonus semantic checking fn bType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i13) Instruction { const umm = @bitCast(u13, imm); - if (umm % 2 != 0) @panic("Internal error: misaligned branch target"); + assert(umm % 2 == 0); // misaligned branch target return Instruction{ .B = .{ @@ -140,15 +142,15 @@ pub const Instruction = union(enum) { } fn jType(op: u7, rd: Register, imm: i21) Instruction { - const umm = @bitcast(u21, imm); - if (umm % 2 != 0) @panic("Internal error: misaligned jump target"); + const umm = @bitCast(u21, imm); + assert(umm % 2 == 0); // misaligned jump target return Instruction{ .J = .{ .opcode = op, .rd = @enumToInt(rd), .imm1_10 = @truncate(u10, umm >> 1), - .imm11 = @truncate(u1, umm >> 1), + .imm11 = @truncate(u1, umm >> 11), .imm12_19 = @truncate(u8, umm >> 12), .imm20 = @truncate(u1, umm >> 20), }, @@ -340,27 +342,27 @@ pub const Instruction = union(enum) { // Branch - pub fn beq(r1: Register, r2: Register, offset: u13) Instruction { + pub fn beq(r1: Register, r2: Register, offset: i13) Instruction { return bType(0b1100011, 0b000, r1, r2, offset); } - pub fn bne(r1: Register, r2: Register, offset: u13) Instruction { + pub fn bne(r1: Register, r2: Register, offset: i13) Instruction { return bType(0b1100011, 0b001, r1, r2, offset); } - pub fn blt(r1: Register, r2: Register, offset: u13) Instruction { + pub fn blt(r1: Register, r2: Register, offset: i13) Instruction { return bType(0b1100011, 0b100, r1, r2, offset); } - pub fn bge(r1: Register, r2: Register, offset: u13) Instruction { + pub fn bge(r1: Register, r2: Register, offset: i13) Instruction { return bType(0b1100011, 0b101, r1, r2, offset); } - pub fn bltu(r1: Register, r2: Register, offset: u13) Instruction { + pub fn bltu(r1: Register, r2: Register, offset: i13) Instruction { return bType(0b1100011, 0b110, r1, r2, offset); } - pub fn bgeu(r1: Register, r2: Register, offset: u13) Instruction { + pub fn bgeu(r1: Register, r2: Register, offset: i13) Instruction { return bType(0b1100011, 0b111, r1, r2, offset); } @@ -431,3 +433,38 @@ pub const Register = enum(u5) { pub const callee_preserved_regs = [_]Register{ .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11, }; + +test "serialize instructions" { + const Testcase = struct { + inst: Instruction, + expected: u32, + }; + + const testcases = [_]Testcase{ + .{ // add t6, zero, zero + .inst = Instruction.add(.t6, .zero, .zero), + .expected = 0b0000000_00000_00000_000_11111_0110011, + }, + .{ // sd s0, 0x7f(s0) + .inst = Instruction.sd(.s0, 0x7f, .s0), + .expected = 0b0000011_01000_01000_011_11111_0100011, + }, + .{ // bne s0, s1, 0x42 + .inst = Instruction.bne(.s0, .s1, 0x42), + .expected = 0b0_000010_01001_01000_001_0001_0_1100011, + }, + .{ // j 0x1a + .inst = Instruction.jal(.zero, 0x1a), + .expected = 0b0_0000001101_0_00000000_00000_1101111, + }, + .{ // ebreak + .inst = Instruction.ebreak, + .expected = 0b000000000001_00000_000_00000_1110011, + }, + }; + + for (testcases) |case| { + const actual = case.inst.toU32(); + testing.expectEqual(case.expected, actual); + } +} diff --git a/zig/src/libc_installation.zig b/zig/src/libc_installation.zig index 6700787925..bb83416d30 100644 --- a/zig/src/libc_installation.zig +++ b/zig/src/libc_installation.zig @@ -9,6 +9,7 @@ const build_options = @import("build_options"); const is_darwin = Target.current.isDarwin(); const is_windows = Target.current.os.tag == .windows; const is_gnu = Target.current.isGnu(); +const is_haiku = Target.current.os.tag == .haiku; const log = std.log.scoped(.libc_installation); @@ -279,8 +280,14 @@ pub const LibCInstallation = struct { return error.CCompilerCannotFindHeaders; } - const include_dir_example_file = "stdlib.h"; - const sys_include_dir_example_file = if (is_windows) "sys\\types.h" else "sys/errno.h"; + const include_dir_example_file = if (is_haiku) "posix/stdlib.h" else "stdlib.h"; + const sys_include_dir_example_file = if (is_windows) + "sys\\types.h" + else if (is_haiku) + "posix/errno.h" + else + "sys/errno.h" + ; var path_i: usize = 0; while (path_i < search_paths.items.len) : (path_i += 1) { diff --git a/zig/src/link/MachO.zig b/zig/src/link/MachO.zig index 9a94b90137..aaf88ad815 100644 --- a/zig/src/link/MachO.zig +++ b/zig/src/link/MachO.zig @@ -645,8 +645,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { break :blk true; } - if (self.base.options.link_libcpp or - self.base.options.output_mode == .Lib or + if (self.base.options.output_mode == .Lib or self.base.options.linker_script != null) { // Fallback to LLD in this handful of cases on x86_64 only. diff --git a/zig/src/link/MachO/Archive.zig b/zig/src/link/MachO/Archive.zig index 86e160ba4d..702a807a4d 100644 --- a/zig/src/link/MachO/Archive.zig +++ b/zig/src/link/MachO/Archive.zig @@ -16,7 +16,7 @@ allocator: *Allocator, arch: ?std.Target.Cpu.Arch = null, file: ?fs.File = null, header: ?ar_hdr = null, -name: ?[]u8 = null, +name: ?[]const u8 = null, /// Parsed table of contents. /// Each symbol name points to a list of all definition @@ -195,7 +195,7 @@ fn parseTableOfContents(self: *Archive, reader: anytype) !void { } /// Caller owns the Object instance. -pub fn parseObject(self: Archive, offset: u32) !Object { +pub fn parseObject(self: Archive, offset: u32) !*Object { var reader = self.file.?.reader(); try reader.context.seekTo(offset); @@ -208,17 +208,19 @@ pub fn parseObject(self: Archive, offset: u32) !Object { const object_name = try parseName(self.allocator, object_header, reader); defer self.allocator.free(object_name); - const object_basename = std.fs.path.basename(object_name); - log.debug("extracting object '{s}' from archive '{s}'", .{ object_basename, self.name.? }); + log.debug("extracting object '{s}' from archive '{s}'", .{ object_name, self.name.? }); const name = name: { var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; const path = try std.os.realpath(self.name.?, &buffer); - break :name try std.fmt.allocPrint(self.allocator, "{s}({s})", .{ path, object_basename }); + break :name try std.fmt.allocPrint(self.allocator, "{s}({s})", .{ path, object_name }); }; - var object = Object.init(self.allocator); + var object = try self.allocator.create(Object); + errdefer self.allocator.destroy(object); + + object.* = Object.init(self.allocator); object.arch = self.arch.?; object.file = try fs.cwd().openFile(self.name.?, .{}); object.name = name; diff --git a/zig/src/link/MachO/Object.zig b/zig/src/link/MachO/Object.zig index 7b5f23756a..4d2ade7aad 100644 --- a/zig/src/link/MachO/Object.zig +++ b/zig/src/link/MachO/Object.zig @@ -22,7 +22,7 @@ arch: ?std.Target.Cpu.Arch = null, header: ?macho.mach_header_64 = null, file: ?fs.File = null, file_offset: ?u32 = null, -name: ?[]u8 = null, +name: ?[]const u8 = null, load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, sections: std.ArrayListUnmanaged(Section) = .{}, @@ -32,7 +32,9 @@ symtab_cmd_index: ?u16 = null, dysymtab_cmd_index: ?u16 = null, build_version_cmd_index: ?u16 = null, data_in_code_cmd_index: ?u16 = null, + text_section_index: ?u16 = null, +mod_init_func_section_index: ?u16 = null, // __DWARF segment sections dwarf_debug_info_index: ?u16 = null, @@ -41,16 +43,13 @@ dwarf_debug_str_index: ?u16 = null, dwarf_debug_line_index: ?u16 = null, dwarf_debug_ranges_index: ?u16 = null, -symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{}, -strtab: std.ArrayListUnmanaged(u8) = .{}, +symbols: std.ArrayListUnmanaged(*Symbol) = .{}, +initializers: std.ArrayListUnmanaged(*Symbol) = .{}, +data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, -locals: std.StringArrayHashMapUnmanaged(Symbol) = .{}, -stabs: std.ArrayListUnmanaged(Stab) = .{}, tu_path: ?[]const u8 = null, tu_mtime: ?u64 = null, -data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, - pub const Section = struct { inner: macho.section_64, code: []u8, @@ -68,18 +67,6 @@ pub const Section = struct { } }; -const Stab = struct { - tag: Tag, - symbol: u32, - size: ?u64 = null, - - const Tag = enum { - function, - global, - static, - }; -}; - const DebugInfo = struct { inner: dwarf.DwarfInfo, debug_info: []u8, @@ -161,15 +148,14 @@ pub fn deinit(self: *Object) void { } self.sections.deinit(self.allocator); - for (self.locals.items()) |*entry| { - entry.value.deinit(self.allocator); + for (self.symbols.items) |sym| { + sym.deinit(self.allocator); + self.allocator.destroy(sym); } - self.locals.deinit(self.allocator); + self.symbols.deinit(self.allocator); - self.symtab.deinit(self.allocator); - self.strtab.deinit(self.allocator); - self.stabs.deinit(self.allocator); self.data_in_code_entries.deinit(self.allocator); + self.initializers.deinit(self.allocator); if (self.name) |n| { self.allocator.free(n); @@ -213,9 +199,10 @@ pub fn parse(self: *Object) !void { } try self.readLoadCommands(reader); + try self.parseSymbols(); try self.parseSections(); - if (self.symtab_cmd_index != null) try self.parseSymtab(); - if (self.data_in_code_cmd_index != null) try self.readDataInCode(); + try self.parseDataInCode(); + try self.parseInitializers(); try self.parseDebugInfo(); } @@ -250,6 +237,10 @@ pub fn readLoadCommands(self: *Object, reader: anytype) !void { if (mem.eql(u8, sectname, "__text")) { self.text_section_index = index; } + } else if (mem.eql(u8, segname, "__DATA")) { + if (mem.eql(u8, sectname, "__mod_init_func")) { + self.mod_init_func_section_index = index; + } } sect.offset += offset; @@ -284,9 +275,10 @@ pub fn readLoadCommands(self: *Object, reader: anytype) !void { } pub fn parseSections(self: *Object) !void { - log.debug("parsing sections in {s}", .{self.name.?}); const seg = self.load_commands.items[self.segment_cmd_index.?].Segment; + log.debug("parsing sections in {s}", .{self.name.?}); + try self.sections.ensureCapacity(self.allocator, seg.sections.items.len); for (seg.sections.items) |sect| { @@ -298,67 +290,119 @@ pub fn parseSections(self: *Object) !void { var section = Section{ .inner = sect, .code = code, - .relocs = undefined, + .relocs = null, }; // Parse relocations - section.relocs = if (sect.nreloc > 0) relocs: { + if (sect.nreloc > 0) { var raw_relocs = try self.allocator.alloc(u8, @sizeOf(macho.relocation_info) * sect.nreloc); defer self.allocator.free(raw_relocs); _ = try self.file.?.preadAll(raw_relocs, sect.reloff); - break :relocs try reloc.parse( + section.relocs = try reloc.parse( self.allocator, self.arch.?, section.code, mem.bytesAsSlice(macho.relocation_info, raw_relocs), + self.symbols.items, ); - } else null; + } self.sections.appendAssumeCapacity(section); } } -pub fn parseSymtab(self: *Object) !void { - const symtab_cmd = self.load_commands.items[self.symtab_cmd_index.?].Symtab; +pub fn parseInitializers(self: *Object) !void { + const index = self.mod_init_func_section_index orelse return; + const section = self.sections.items[index]; + + log.debug("parsing initializers in {s}", .{self.name.?}); + + // Parse C++ initializers + const relocs = section.relocs orelse unreachable; + try self.initializers.ensureCapacity(self.allocator, relocs.len); + for (relocs) |rel| { + self.initializers.appendAssumeCapacity(rel.target.symbol); + } + + mem.reverse(*Symbol, self.initializers.items); +} + +pub fn parseSymbols(self: *Object) !void { + const index = self.symtab_cmd_index orelse return; + const symtab_cmd = self.load_commands.items[index].Symtab; var symtab = try self.allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms); defer self.allocator.free(symtab); - _ = try self.file.?.preadAll(symtab, symtab_cmd.symoff); const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, symtab)); - try self.symtab.appendSlice(self.allocator, slice); var strtab = try self.allocator.alloc(u8, symtab_cmd.strsize); defer self.allocator.free(strtab); - _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff); - try self.strtab.appendSlice(self.allocator, strtab); - for (self.symtab.items) |sym, sym_id| { - if (Symbol.isStab(sym) or Symbol.isUndef(sym)) continue; + for (slice) |sym| { + const sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx)); + + if (Symbol.isStab(sym)) { + log.err("stab {s} in {s}", .{ sym_name, self.name.? }); + return error.UnhandledSymbolType; + } + if (Symbol.isIndr(sym)) { + log.err("indirect symbol {s} in {s}", .{ sym_name, self.name.? }); + return error.UnhandledSymbolType; + } + if (Symbol.isAbs(sym)) { + log.err("absolute symbol {s} in {s}", .{ sym_name, self.name.? }); + return error.UnhandledSymbolType; + } - const sym_name = self.getString(sym.n_strx); - const tag: Symbol.Tag = tag: { - if (Symbol.isLocal(sym)) { - if (self.arch.? == .aarch64 and mem.startsWith(u8, sym_name, "l")) continue; - break :tag .local; + const name = try self.allocator.dupe(u8, sym_name); + const symbol: *Symbol = symbol: { + if (Symbol.isSect(sym)) { + const linkage: Symbol.Regular.Linkage = linkage: { + if (!Symbol.isExt(sym)) break :linkage .translation_unit; + if (Symbol.isWeakDef(sym) or Symbol.isPext(sym)) break :linkage .linkage_unit; + break :linkage .global; + }; + const regular = try self.allocator.create(Symbol.Regular); + errdefer self.allocator.destroy(regular); + regular.* = .{ + .base = .{ + .@"type" = .regular, + .name = name, + }, + .linkage = linkage, + .address = sym.n_value, + .section = sym.n_sect - 1, + .weak_ref = Symbol.isWeakRef(sym), + .file = self, + }; + break :symbol ®ular.base; } - if (Symbol.isWeakDef(sym)) { - break :tag .weak; + + if (sym.n_value != 0) { + log.err("common symbol {s} in {s}", .{ sym_name, self.name.? }); + return error.UnhandledSymbolType; + // const comm_size = sym.n_value; + // const comm_align = (sym.n_desc >> 8) & 0x0f; + // log.warn("Common symbol: size 0x{x}, align 0x{x}", .{ comm_size, comm_align }); } - break :tag .strong; + + const undef = try self.allocator.create(Symbol.Unresolved); + errdefer self.allocator.destroy(undef); + undef.* = .{ + .base = .{ + .@"type" = .unresolved, + .name = name, + }, + .file = self, + }; + break :symbol &undef.base; }; - const name = try self.allocator.dupe(u8, sym_name); - try self.locals.putNoClobber(self.allocator, name, .{ - .tag = tag, - .name = name, - .address = 0, - .section = 0, - .index = @intCast(u32, sym_id), - }); + try self.symbols.append(self.allocator, symbol); } } @@ -390,38 +434,31 @@ pub fn parseDebugInfo(self: *Object) !void { break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000)); }; - for (self.locals.items()) |entry, index| { - const local = entry.value; - const source_sym = self.symtab.items[local.index.?]; - const size = blk: for (debug_info.inner.func_list.items) |func| { - if (func.pc_range) |range| { - if (source_sym.n_value >= range.start and source_sym.n_value < range.end) { - break :blk range.end - range.start; + for (self.symbols.items) |sym| { + if (sym.cast(Symbol.Regular)) |reg| { + const size: u64 = blk: for (debug_info.inner.func_list.items) |func| { + if (func.pc_range) |range| { + if (reg.address >= range.start and reg.address < range.end) { + break :blk range.end - range.start; + } } - } - } else null; - const tag: Stab.Tag = tag: { - if (size != null) break :tag .function; - switch (local.tag) { - .weak, .strong => break :tag .global, - else => break :tag .static, - } - }; - - try self.stabs.append(self.allocator, .{ - .tag = tag, - .size = size, - .symbol = @intCast(u32, index), - }); + } else 0; + + reg.stab = .{ + .kind = kind: { + if (size > 0) break :kind .function; + switch (reg.linkage) { + .translation_unit => break :kind .static, + else => break :kind .global, + } + }, + .size = size, + }; + } } } -pub fn getString(self: *const Object, str_off: u32) []const u8 { - assert(str_off < self.strtab.items.len); - return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + str_off)); -} - -pub fn readSection(self: Object, allocator: *Allocator, index: u16) ![]u8 { +fn readSection(self: Object, allocator: *Allocator, index: u16) ![]u8 { const seg = self.load_commands.items[self.segment_cmd_index.?].Segment; const sect = seg.sections.items[index]; var buffer = try allocator.alloc(u8, sect.size); @@ -429,7 +466,7 @@ pub fn readSection(self: Object, allocator: *Allocator, index: u16) ![]u8 { return buffer; } -pub fn readDataInCode(self: *Object) !void { +pub fn parseDataInCode(self: *Object) !void { const index = self.data_in_code_cmd_index orelse return; const data_in_code = self.load_commands.items[index].LinkeditData; diff --git a/zig/src/link/MachO/Symbol.zig b/zig/src/link/MachO/Symbol.zig index f65a694f75..f928c807a3 100644 --- a/zig/src/link/MachO/Symbol.zig +++ b/zig/src/link/MachO/Symbol.zig @@ -2,31 +2,113 @@ const Symbol = @This(); const std = @import("std"); const macho = std.macho; +const mem = std.mem; -const Allocator = std.mem.Allocator; +const Allocator = mem.Allocator; +const Object = @import("Object.zig"); -pub const Tag = enum { - local, - weak, - strong, - import, - undef, +pub const Type = enum { + regular, + proxy, + unresolved, }; -tag: Tag, +/// Symbol type. +@"type": Type, + +/// Symbol name. Owned slice. name: []u8, -address: u64, -section: u8, -/// Index of file where to locate this symbol. -/// Depending on context, this is either an object file, or a dylib. -file: ?u16 = null, +/// Alias of. +alias: ?*Symbol = null, + +/// Index in GOT table for indirection. +got_index: ?u32 = null, + +/// Index in stubs table for late binding. +stubs_index: ?u32 = null, + +pub const Regular = struct { + base: Symbol, + + /// Linkage type. + linkage: Linkage, + + /// Symbol address. + address: u64, + + /// Section ID where the symbol resides. + section: u8, + + /// Whether the symbol is a weak ref. + weak_ref: bool, + + /// File where to locate this symbol. + file: *Object, + + /// Debug stab if defined. + stab: ?struct { + /// Stab kind + kind: enum { + function, + global, + static, + }, + + /// Size of the stab. + size: u64, + } = null, + + pub const base_type: Symbol.Type = .regular; + + pub const Linkage = enum { + translation_unit, + linkage_unit, + global, + }; + + pub fn isTemp(regular: *Regular) bool { + if (regular.linkage == .translation_unit) { + return mem.startsWith(u8, regular.base.name, "l") or mem.startsWith(u8, regular.base.name, "L"); + } + return false; + } +}; + +pub const Proxy = struct { + base: Symbol, + + /// Dylib ordinal. + dylib: u16, + + pub const base_type: Symbol.Type = .proxy; +}; + +pub const Unresolved = struct { + base: Symbol, + + /// File where this symbol was referenced. + file: *Object, -/// Index of this symbol within the file's symbol table. -index: ?u32 = null, + pub const base_type: Symbol.Type = .unresolved; +}; -pub fn deinit(self: *Symbol, allocator: *Allocator) void { - allocator.free(self.name); +pub fn deinit(base: *Symbol, allocator: *Allocator) void { + allocator.free(base.name); +} + +pub fn cast(base: *Symbol, comptime T: type) ?*T { + if (base.@"type" != T.base_type) { + return null; + } + return @fieldParentPtr(T, "base", base); +} + +pub fn getTopmostAlias(base: *Symbol) *Symbol { + if (base.alias) |alias| { + return alias.getTopmostAlias(); + } + return base; } pub fn isStab(sym: macho.nlist_64) bool { @@ -51,21 +133,20 @@ pub fn isUndf(sym: macho.nlist_64) bool { return type_ == macho.N_UNDF; } -pub fn isWeakDef(sym: macho.nlist_64) bool { - return sym.n_desc == macho.N_WEAK_DEF; +pub fn isIndr(sym: macho.nlist_64) bool { + const type_ = macho.N_TYPE & sym.n_type; + return type_ == macho.N_INDR; } -/// Symbol is local if it is defined and not an extern. -pub fn isLocal(sym: macho.nlist_64) bool { - return isSect(sym) and !isExt(sym); +pub fn isAbs(sym: macho.nlist_64) bool { + const type_ = macho.N_TYPE & sym.n_type; + return type_ == macho.N_ABS; } -/// Symbol is global if it is defined and an extern. -pub fn isGlobal(sym: macho.nlist_64) bool { - return isSect(sym) and isExt(sym); +pub fn isWeakDef(sym: macho.nlist_64) bool { + return (sym.n_desc & macho.N_WEAK_DEF) != 0; } -/// Symbol is undefined if it is not defined and an extern. -pub fn isUndef(sym: macho.nlist_64) bool { - return isUndf(sym) and isExt(sym); +pub fn isWeakRef(sym: macho.nlist_64) bool { + return (sym.n_desc & macho.N_WEAK_REF) != 0; } diff --git a/zig/src/link/MachO/Zld.zig b/zig/src/link/MachO/Zld.zig index ae475ab30d..4d19da1e97 100644 --- a/zig/src/link/MachO/Zld.zig +++ b/zig/src/link/MachO/Zld.zig @@ -29,8 +29,8 @@ page_size: ?u16 = null, file: ?fs.File = null, out_path: ?[]const u8 = null, -objects: std.ArrayListUnmanaged(Object) = .{}, -archives: std.ArrayListUnmanaged(Archive) = .{}, +objects: std.ArrayListUnmanaged(*Object) = .{}, +archives: std.ArrayListUnmanaged(*Archive) = .{}, load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, @@ -58,6 +58,7 @@ stubs_section_index: ?u16 = null, stub_helper_section_index: ?u16 = null, text_const_section_index: ?u16 = null, cstring_section_index: ?u16 = null, +ustring_section_index: ?u16 = null, // __DATA_CONST segment sections got_section_index: ?u16 = null, @@ -72,29 +73,32 @@ tlv_bss_section_index: ?u16 = null, la_symbol_ptr_section_index: ?u16 = null, data_section_index: ?u16 = null, bss_section_index: ?u16 = null, +common_section_index: ?u16 = null, + +globals: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, +imports: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, +unresolved: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, -symtab: std.StringArrayHashMapUnmanaged(Symbol) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, strtab_dir: std.StringHashMapUnmanaged(u32) = .{}, -threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{}, +threadlocal_offsets: std.ArrayListUnmanaged(TlvOffset) = .{}, // TODO merge with Symbol abstraction local_rebases: std.ArrayListUnmanaged(Pointer) = .{}, -stubs: std.StringArrayHashMapUnmanaged(u32) = .{}, -got_entries: std.StringArrayHashMapUnmanaged(GotEntry) = .{}, +stubs: std.ArrayListUnmanaged(*Symbol) = .{}, +got_entries: std.ArrayListUnmanaged(*Symbol) = .{}, stub_helper_stubs_start_off: ?u64 = null, mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{}, unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{}, -const GotEntry = struct { - tag: enum { - local, - import, - }, - index: u32, - target_addr: u64, - file: u16, +const TlvOffset = struct { + source_addr: u64, + offset: u64, + + fn cmp(context: void, a: TlvOffset, b: TlvOffset) bool { + return a.source_addr < b.source_addr; + } }; const MappingKey = struct { @@ -123,15 +127,7 @@ pub fn init(allocator: *Allocator) Zld { pub fn deinit(self: *Zld) void { self.threadlocal_offsets.deinit(self.allocator); self.local_rebases.deinit(self.allocator); - - for (self.stubs.items()) |entry| { - self.allocator.free(entry.key); - } self.stubs.deinit(self.allocator); - - for (self.got_entries.items()) |entry| { - self.allocator.free(entry.key); - } self.got_entries.deinit(self.allocator); for (self.load_commands.items) |*lc| { @@ -139,23 +135,22 @@ pub fn deinit(self: *Zld) void { } self.load_commands.deinit(self.allocator); - for (self.objects.items) |*object| { + for (self.objects.items) |object| { object.deinit(); + self.allocator.destroy(object); } self.objects.deinit(self.allocator); - for (self.archives.items) |*archive| { + for (self.archives.items) |archive| { archive.deinit(); + self.allocator.destroy(archive); } self.archives.deinit(self.allocator); self.mappings.deinit(self.allocator); self.unhandled_sections.deinit(self.allocator); - for (self.symtab.items()) |*entry| { - entry.value.deinit(self.allocator); - } - self.symtab.deinit(self.allocator); + self.globals.deinit(self.allocator); self.strtab.deinit(self.allocator); { @@ -223,9 +218,6 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void { try self.allocateDataSegment(); self.allocateLinkeditSegment(); try self.allocateSymbols(); - try self.allocateStubsAndGotEntries(); - try self.writeStubHelperCommon(); - try self.resolveRelocsAndWriteSections(); try self.flush(); } @@ -289,17 +281,23 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { for (classified.items) |input| { switch (input.kind) { .object => { - var object = Object.init(self.allocator); + const object = try self.allocator.create(Object); + errdefer self.allocator.destroy(object); + + object.* = Object.init(self.allocator); object.arch = self.arch.?; - object.name = try self.allocator.dupe(u8, input.name); + object.name = input.name; object.file = input.file; try object.parse(); try self.objects.append(self.allocator, object); }, .archive => { - var archive = Archive.init(self.allocator); + const archive = try self.allocator.create(Archive); + errdefer self.allocator.destroy(archive); + + archive.* = Archive.init(self.allocator); archive.arch = self.arch.?; - archive.name = try self.allocator.dupe(u8, input.name); + archive.name = input.name; archive.file = input.file; try archive.parse(); try self.archives.append(self.allocator, archive); @@ -365,23 +363,43 @@ fn updateMetadata(self: *Zld) !void { switch (flags) { macho.S_REGULAR, macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => { if (mem.eql(u8, segname, "__TEXT")) { - if (self.text_const_section_index != null) continue; - - self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__const"), - .segname = makeStaticString("__TEXT"), - .addr = 0, - .size = 0, - .offset = 0, - .@"align" = 0, - .reloff = 0, - .nreloc = 0, - .flags = macho.S_REGULAR, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, - }); + if (mem.eql(u8, sectname, "__ustring")) { + if (self.ustring_section_index != null) continue; + + self.ustring_section_index = @intCast(u16, text_seg.sections.items.len); + try text_seg.addSection(self.allocator, .{ + .sectname = makeStaticString("__ustring"), + .segname = makeStaticString("__TEXT"), + .addr = 0, + .size = 0, + .offset = 0, + .@"align" = 0, + .reloff = 0, + .nreloc = 0, + .flags = macho.S_REGULAR, + .reserved1 = 0, + .reserved2 = 0, + .reserved3 = 0, + }); + } else { + if (self.text_const_section_index != null) continue; + + self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); + try text_seg.addSection(self.allocator, .{ + .sectname = makeStaticString("__const"), + .segname = makeStaticString("__TEXT"), + .addr = 0, + .size = 0, + .offset = 0, + .@"align" = 0, + .reloff = 0, + .nreloc = 0, + .flags = macho.S_REGULAR, + .reserved1 = 0, + .reserved2 = 0, + .reserved3 = 0, + }); + } } else if (mem.eql(u8, segname, "__DATA")) { if (!mem.eql(u8, sectname, "__const")) continue; if (self.data_const_section_index != null) continue; @@ -465,23 +483,43 @@ fn updateMetadata(self: *Zld) !void { }, macho.S_ZEROFILL => { if (!mem.eql(u8, segname, "__DATA")) continue; - if (self.bss_section_index != null) continue; + if (mem.eql(u8, sectname, "__common")) { + if (self.common_section_index != null) continue; - self.bss_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__bss"), - .segname = makeStaticString("__DATA"), - .addr = 0, - .size = 0, - .offset = 0, - .@"align" = 0, - .reloff = 0, - .nreloc = 0, - .flags = macho.S_ZEROFILL, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, - }); + self.common_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.addSection(self.allocator, .{ + .sectname = makeStaticString("__common"), + .segname = makeStaticString("__DATA"), + .addr = 0, + .size = 0, + .offset = 0, + .@"align" = 0, + .reloff = 0, + .nreloc = 0, + .flags = macho.S_ZEROFILL, + .reserved1 = 0, + .reserved2 = 0, + .reserved3 = 0, + }); + } else { + if (self.bss_section_index != null) continue; + + self.bss_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.addSection(self.allocator, .{ + .sectname = makeStaticString("__bss"), + .segname = makeStaticString("__DATA"), + .addr = 0, + .size = 0, + .offset = 0, + .@"align" = 0, + .reloff = 0, + .nreloc = 0, + .flags = macho.S_ZEROFILL, + .reserved1 = 0, + .reserved2 = 0, + .reserved3 = 0, + }); + } }, macho.S_THREAD_LOCAL_VARIABLES => { if (!mem.eql(u8, segname, "__DATA")) continue; @@ -568,13 +606,59 @@ fn updateMetadata(self: *Zld) !void { const segname = parseName(&source_sect.segname); const sectname = parseName(&source_sect.sectname); + log.debug("section '{s}/{s}' will be unmapped", .{ segname, sectname }); + try self.unhandled_sections.putNoClobber(self.allocator, .{ .object_id = object_id, .source_sect_id = source_sect_id, }, 0); } } + + tlv_align: { + const has_tlv = + self.tlv_section_index != null or + self.tlv_data_section_index != null or + self.tlv_bss_section_index != null; + + if (!has_tlv) break :tlv_align; + + const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; + + if (self.tlv_section_index) |index| { + const sect = &seg.sections.items[index]; + sect.@"align" = 3; // __thread_vars is always 8byte aligned + } + + // Apparently __tlv_data and __tlv_bss need to have matching alignment, so fix it up. + // All __thread_data and __thread_bss sections must have same alignment + // https://github.com/apple-opensource/ld64/blob/e28c028b20af187a16a7161d89e91868a450cadc/src/ld/ld.cpp#L1172 + const data_align: u32 = data: { + if (self.tlv_data_section_index) |index| { + const sect = &seg.sections.items[index]; + break :data sect.@"align"; + } + break :tlv_align; + }; + const bss_align: u32 = bss: { + if (self.tlv_bss_section_index) |index| { + const sect = &seg.sections.items[index]; + break :bss sect.@"align"; + } + break :tlv_align; + }; + const max_align = math.max(data_align, bss_align); + + if (self.tlv_data_section_index) |index| { + const sect = &seg.sections.items[index]; + sect.@"align" = max_align; + } + if (self.tlv_bss_section_index) |index| { + const sect = &seg.sections.items[index]; + sect.@"align" = max_align; + } + } } const MatchingSection = struct { @@ -585,6 +669,7 @@ const MatchingSection = struct { fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection { const segname = parseName(§ion.segname); const sectname = parseName(§ion.sectname); + const res: ?MatchingSection = blk: { switch (section.flags) { macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => { @@ -612,6 +697,12 @@ fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection { }; }, macho.S_ZEROFILL => { + if (mem.eql(u8, sectname, "__common")) { + break :blk .{ + .seg = self.data_segment_cmd_index.?, + .sect = self.common_section_index.?, + }; + } break :blk .{ .seg = self.data_segment_cmd_index.?, .sect = self.bss_section_index.?, @@ -643,10 +734,17 @@ fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection { }, macho.S_REGULAR => { if (mem.eql(u8, segname, "__TEXT")) { - break :blk .{ - .seg = self.text_segment_cmd_index.?, - .sect = self.text_const_section_index.?, - }; + if (mem.eql(u8, sectname, "__ustring")) { + break :blk .{ + .seg = self.text_segment_cmd_index.?, + .sect = self.ustring_section_index.?, + }; + } else { + break :blk .{ + .seg = self.text_segment_cmd_index.?, + .sect = self.text_const_section_index.?, + }; + } } else if (mem.eql(u8, segname, "__DATA")) { if (mem.eql(u8, sectname, "__data")) { break :blk .{ @@ -667,6 +765,7 @@ fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection { }, } }; + return res; } @@ -691,6 +790,7 @@ fn sortSections(self: *Zld) !void { &self.stub_helper_section_index, &self.text_const_section_index, &self.cstring_section_index, + &self.ustring_section_index, }; for (indices) |maybe_index| { const new_index: u16 = if (maybe_index.*) |index| blk: { @@ -737,11 +837,12 @@ fn sortSections(self: *Zld) !void { // __DATA segment const indices = &[_]*?u16{ &self.la_symbol_ptr_section_index, - &self.tlv_section_index, &self.data_section_index, + &self.tlv_section_index, &self.tlv_data_section_index, &self.tlv_bss_section_index, &self.bss_section_index, + &self.common_section_index, }; for (indices) |maybe_index| { const new_index: u16 = if (maybe_index.*) |index| blk: { @@ -772,7 +873,7 @@ fn sortSections(self: *Zld) !void { fn allocateTextSegment(self: *Zld) !void { const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; - const nstubs = @intCast(u32, self.stubs.items().len); + const nstubs = @intCast(u32, self.stubs.items.len); const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize; seg.inner.fileoff = 0; @@ -823,7 +924,7 @@ fn allocateTextSegment(self: *Zld) !void { fn allocateDataConstSegment(self: *Zld) !void { const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; - const nentries = @intCast(u32, self.got_entries.items().len); + const nentries = @intCast(u32, self.got_entries.items.len); const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize; @@ -838,7 +939,7 @@ fn allocateDataConstSegment(self: *Zld) !void { fn allocateDataSegment(self: *Zld) !void { const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; - const nstubs = @intCast(u32, self.stubs.items().len); + const nstubs = @intCast(u32, self.stubs.items.len); const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize; @@ -881,82 +982,48 @@ fn allocateSegment(self: *Zld, index: u16, offset: u64) !void { } fn allocateSymbols(self: *Zld) !void { - for (self.objects.items) |*object, object_id| { - for (object.locals.items()) |*entry| { - const source_sym = object.symtab.items[entry.value.index.?]; - const source_sect_id = source_sym.n_sect - 1; + for (self.objects.items) |object, object_id| { + for (object.symbols.items) |sym| { + const reg = sym.cast(Symbol.Regular) orelse continue; // TODO I am more and more convinced we should store the mapping as part of the Object struct. const target_mapping = self.mappings.get(.{ .object_id = @intCast(u16, object_id), - .source_sect_id = source_sect_id, + .source_sect_id = reg.section, }) orelse { if (self.unhandled_sections.get(.{ .object_id = @intCast(u16, object_id), - .source_sect_id = source_sect_id, + .source_sect_id = reg.section, }) != null) continue; - log.err("section not mapped for symbol '{s}'", .{entry.value.name}); + log.err("section not mapped for symbol '{s}'", .{sym.name}); return error.SectionNotMappedForSymbol; }; const source_seg = object.load_commands.items[object.segment_cmd_index.?].Segment; - const source_sect = source_seg.sections.items[source_sect_id]; + const source_sect = source_seg.sections.items[reg.section]; const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment; const target_sect = target_seg.sections.items[target_mapping.target_sect_id]; const target_addr = target_sect.addr + target_mapping.offset; - const n_value = source_sym.n_value - source_sect.addr + target_addr; + const address = reg.address - source_sect.addr + target_addr; - log.debug("resolving local symbol '{s}' at 0x{x}", .{ entry.value.name, n_value }); + log.debug("resolving symbol '{s}' at 0x{x}", .{ sym.name, address }); // TODO there might be a more generic way of doing this. - var n_sect: u8 = 0; + var section: u8 = 0; for (self.load_commands.items) |cmd, cmd_id| { if (cmd != .Segment) break; if (cmd_id == target_mapping.target_seg_id) { - n_sect += @intCast(u8, target_mapping.target_sect_id) + 1; + section += @intCast(u8, target_mapping.target_sect_id) + 1; break; } - n_sect += @intCast(u8, cmd.Segment.sections.items.len); + section += @intCast(u8, cmd.Segment.sections.items.len); } - entry.value.address = n_value; - entry.value.section = n_sect; + reg.address = address; + reg.section = section; } } - - for (self.symtab.items()) |*entry| { - if (entry.value.tag == .import) continue; - - const object_id = entry.value.file orelse unreachable; - const object = self.objects.items[object_id]; - const local = object.locals.get(entry.key) orelse unreachable; - - log.debug("resolving {} symbol '{s}' at 0x{x}", .{ entry.value.tag, entry.key, local.address }); - - entry.value.address = local.address; - entry.value.section = local.section; - } -} - -fn allocateStubsAndGotEntries(self: *Zld) !void { - for (self.got_entries.items()) |*entry| { - if (entry.value.tag == .import) continue; - - const object = self.objects.items[entry.value.file]; - entry.value.target_addr = target_addr: { - if (object.locals.get(entry.key)) |local| { - break :target_addr local.address; - } - const global = self.symtab.get(entry.key) orelse unreachable; - break :target_addr global.address; - }; - - log.debug("resolving GOT entry '{s}' at 0x{x}", .{ - entry.key, - entry.value.target_addr, - }); - } } fn writeStubHelperCommon(self: *Zld) !void { @@ -989,8 +1056,8 @@ fn writeStubHelperCommon(self: *Zld) !void { code[9] = 0xff; code[10] = 0x25; { - const dyld_stub_binder = self.got_entries.get("dyld_stub_binder").?; - const addr = (got.addr + dyld_stub_binder.index * @sizeOf(u64)); + const dyld_stub_binder = self.imports.get("dyld_stub_binder").?; + const addr = (got.addr + dyld_stub_binder.got_index.? * @sizeOf(u64)); const displacement = try math.cast(u32, addr - stub_helper.addr - code_size); mem.writeIntLittle(u32, code[11..], displacement); } @@ -1033,9 +1100,9 @@ fn writeStubHelperCommon(self: *Zld) !void { code[10] = 0xbf; code[11] = 0xa9; binder_blk_outer: { - const dyld_stub_binder = self.got_entries.get("dyld_stub_binder").?; + const dyld_stub_binder = self.imports.get("dyld_stub_binder").?; const this_addr = stub_helper.addr + 3 * @sizeOf(u32); - const target_addr = (got.addr + dyld_stub_binder.index * @sizeOf(u64)); + const target_addr = (got.addr + dyld_stub_binder.got_index.? * @sizeOf(u64)); binder_blk: { const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :binder_blk; const literal = math.cast(u18, displacement) catch |_| break :binder_blk; @@ -1086,8 +1153,9 @@ fn writeStubHelperCommon(self: *Zld) !void { } }; - for (self.stubs.items()) |entry| { - const index = entry.value; + for (self.stubs.items) |sym| { + // TODO weak bound pointers + const index = sym.stubs_index orelse unreachable; try self.writeLazySymbolPointer(index); try self.writeStub(index); try self.writeStubInStubHelper(index); @@ -1226,124 +1294,151 @@ fn writeStubInStubHelper(self: *Zld, index: u32) !void { try self.file.?.pwriteAll(code, stub_off); } -fn resolveSymbolsInObject(self: *Zld, object_id: u16) !void { - const object = self.objects.items[object_id]; +fn resolveSymbolsInObject(self: *Zld, object: *Object) !void { log.debug("resolving symbols in '{s}'", .{object.name}); - for (object.symtab.items) |sym, sym_id| { - if (Symbol.isLocal(sym)) { - // If symbol is local to CU, we don't put it in the global symbol table. - continue; - } else if (Symbol.isGlobal(sym)) { - const sym_name = object.getString(sym.n_strx); - const global = self.symtab.getEntry(sym_name) orelse { + for (object.symbols.items) |sym| { + if (sym.cast(Symbol.Regular)) |reg| { + if (reg.linkage == .translation_unit) continue; // Symbol local to TU. + + if (self.unresolved.swapRemove(sym.name)) |entry| { + // Create link to the global. + entry.value.alias = sym; + } + const entry = self.globals.getEntry(sym.name) orelse { // Put new global symbol into the symbol table. - const name = try self.allocator.dupe(u8, sym_name); - try self.symtab.putNoClobber(self.allocator, name, .{ - .tag = if (Symbol.isWeakDef(sym)) .weak else .strong, - .name = name, - .address = 0, - .section = 0, - .file = object_id, - .index = @intCast(u32, sym_id), - }); + try self.globals.putNoClobber(self.allocator, sym.name, sym); continue; }; - - switch (global.value.tag) { - .weak => continue, // If symbol is weak, nothing to do. - .strong => { - log.err("symbol '{s}' defined multiple times", .{sym_name}); - return error.MultipleSymbolDefinitions; + const g_sym = entry.value; + const g_reg = g_sym.cast(Symbol.Regular) orelse unreachable; + + switch (g_reg.linkage) { + .translation_unit => unreachable, + .linkage_unit => { + if (reg.linkage == .linkage_unit) { + // Create link to the first encountered linkage_unit symbol. + sym.alias = g_sym; + continue; + } + }, + .global => { + if (reg.linkage == .global) { + log.debug("symbol '{s}' defined multiple times", .{reg.base.name}); + return error.MultipleSymbolDefinitions; + } + sym.alias = g_sym; + continue; }, - else => {}, } - global.value.tag = .strong; - global.value.file = object_id; - global.value.index = @intCast(u32, sym_id); - } else if (Symbol.isUndef(sym)) { - const sym_name = object.getString(sym.n_strx); - if (self.symtab.contains(sym_name)) continue; // Nothing to do if we already found a definition. - - const name = try self.allocator.dupe(u8, sym_name); - try self.symtab.putNoClobber(self.allocator, name, .{ - .tag = .undef, - .name = name, - .address = 0, - .section = 0, - }); + g_sym.alias = sym; + entry.value = sym; + } else if (sym.cast(Symbol.Unresolved)) |und| { + if (self.globals.get(sym.name)) |g_sym| { + sym.alias = g_sym; + continue; + } + if (self.unresolved.get(sym.name)) |u_sym| { + sym.alias = u_sym; + continue; + } + try self.unresolved.putNoClobber(self.allocator, sym.name, sym); } else unreachable; } } fn resolveSymbols(self: *Zld) !void { // First pass, resolve symbols in provided objects. - for (self.objects.items) |object, object_id| { - try self.resolveSymbolsInObject(@intCast(u16, object_id)); + for (self.objects.items) |object| { + try self.resolveSymbolsInObject(object); } // Second pass, resolve symbols in static libraries. var next_sym: usize = 0; - var nsyms: usize = self.symtab.items().len; - while (next_sym < nsyms) : (next_sym += 1) { - const sym = self.symtab.items()[next_sym]; - if (sym.value.tag != .undef) continue; + while (true) { + if (next_sym == self.unresolved.count()) break; + + const entry = self.unresolved.items()[next_sym]; + const sym = entry.value; - const sym_name = sym.value.name; + var reset: bool = false; for (self.archives.items) |archive| { // Check if the entry exists in a static archive. - const offsets = archive.toc.get(sym_name) orelse { + const offsets = archive.toc.get(sym.name) orelse { // No hit. continue; }; assert(offsets.items.len > 0); const object = try archive.parseObject(offsets.items[0]); - const object_id = @intCast(u16, self.objects.items.len); try self.objects.append(self.allocator, object); - try self.resolveSymbolsInObject(object_id); + try self.resolveSymbolsInObject(object); - nsyms = self.symtab.items().len; + reset = true; break; } + + if (reset) { + next_sym = 0; + } else { + next_sym += 1; + } } // Third pass, resolve symbols in dynamic libraries. // TODO Implement libSystem as a hard-coded library, or ship with // a libSystem.B.tbd definition file? - for (self.symtab.items()) |*entry| { - if (entry.value.tag != .undef) continue; + try self.imports.ensureCapacity(self.allocator, self.unresolved.count()); + for (self.unresolved.items()) |entry| { + const proxy = try self.allocator.create(Symbol.Proxy); + errdefer self.allocator.destroy(proxy); + + proxy.* = .{ + .base = .{ + .@"type" = .proxy, + .name = try self.allocator.dupe(u8, entry.key), + }, + .dylib = 0, + }; - entry.value.tag = .import; - entry.value.file = 0; + self.imports.putAssumeCapacityNoClobber(proxy.base.name, &proxy.base); + entry.value.alias = &proxy.base; } + self.unresolved.clearAndFree(self.allocator); // If there are any undefs left, flag an error. - var has_unresolved = false; - for (self.symtab.items()) |entry| { - if (entry.value.tag != .undef) continue; - - has_unresolved = true; - log.err("undefined reference to symbol '{s}'", .{entry.value.name}); - } - if (has_unresolved) { + if (self.unresolved.count() > 0) { + for (self.unresolved.items()) |entry| { + log.err("undefined reference to symbol '{s}'", .{entry.key}); + log.err(" | referenced in {s}", .{ + entry.value.cast(Symbol.Unresolved).?.file.name.?, + }); + } return error.UndefinedSymbolReference; } // Finally put dyld_stub_binder as an Import - var name = try self.allocator.dupe(u8, "dyld_stub_binder"); - try self.symtab.putNoClobber(self.allocator, name, .{ - .tag = .import, - .name = name, - .address = 0, - .section = 0, - .file = 0, - }); + const dyld_stub_binder = try self.allocator.create(Symbol.Proxy); + errdefer self.allocator.destroy(dyld_stub_binder); + + dyld_stub_binder.* = .{ + .base = .{ + .@"type" = .proxy, + .name = try self.allocator.dupe(u8, "dyld_stub_binder"), + }, + .dylib = 0, + }; + + try self.imports.putNoClobber( + self.allocator, + dyld_stub_binder.base.name, + &dyld_stub_binder.base, + ); } fn resolveStubsAndGotEntries(self: *Zld) !void { - for (self.objects.items) |object, object_id| { + for (self.objects.items) |object| { log.debug("resolving stubs and got entries from {s}", .{object.name}); for (object.sections.items) |sect| { @@ -1352,42 +1447,32 @@ fn resolveStubsAndGotEntries(self: *Zld) !void { switch (rel.@"type") { .unsigned => continue, .got_page, .got_page_off, .got_load, .got => { - const sym = object.symtab.items[rel.target.symbol]; - const sym_name = object.getString(sym.n_strx); - - if (self.got_entries.contains(sym_name)) continue; - - // TODO clean this up - const is_import = self.symtab.get(sym_name).?.tag == .import; - var name = try self.allocator.dupe(u8, sym_name); - const index = @intCast(u32, self.got_entries.items().len); - try self.got_entries.putNoClobber(self.allocator, name, .{ - .tag = if (is_import) .import else .local, - .index = index, - .target_addr = 0, - .file = if (is_import) 0 else @intCast(u16, object_id), - }); + const sym = rel.target.symbol.getTopmostAlias(); + if (sym.got_index != null) continue; + + const index = @intCast(u32, self.got_entries.items.len); + sym.got_index = index; + try self.got_entries.append(self.allocator, sym); - log.debug(" | found GOT entry {s}: {}", .{ sym_name, self.got_entries.get(sym_name) }); + log.debug(" | found GOT entry {s}: {*}", .{ sym.name, sym }); }, else => { if (rel.target != .symbol) continue; - const sym = object.symtab.items[rel.target.symbol]; - const sym_name = object.getString(sym.n_strx); - - if (!Symbol.isUndef(sym)) continue; - - const in_globals = self.symtab.get(sym_name) orelse unreachable; + const sym = rel.target.symbol.getTopmostAlias(); + assert(sym.@"type" != .unresolved); - if (in_globals.tag != .import) continue; - if (self.stubs.contains(sym_name)) continue; + if (sym.stubs_index != null) continue; + if (sym.@"type" != .proxy) continue; + // if (sym.cast(Symbol.Regular)) |reg| { + // if (!reg.weak_ref) continue; + // } - var name = try self.allocator.dupe(u8, sym_name); - const index = @intCast(u32, self.stubs.items().len); - try self.stubs.putNoClobber(self.allocator, name, index); + const index = @intCast(u32, self.stubs.items.len); + sym.stubs_index = index; + try self.stubs.append(self.allocator, sym); - log.debug(" | found stub {s}: {}", .{ sym_name, self.stubs.get(sym_name) }); + log.debug(" | found stub {s}: {*}", .{ sym.name, sym }); }, } } @@ -1395,16 +1480,12 @@ fn resolveStubsAndGotEntries(self: *Zld) !void { } // Finally, put dyld_stub_binder as the final GOT entry - var name = try self.allocator.dupe(u8, "dyld_stub_binder"); - const index = @intCast(u32, self.got_entries.items().len); - try self.got_entries.putNoClobber(self.allocator, name, .{ - .tag = .import, - .index = index, - .target_addr = 0, - .file = 0, - }); + const sym = self.imports.get("dyld_stub_binder") orelse unreachable; + const index = @intCast(u32, self.got_entries.items.len); + sym.got_index = index; + try self.got_entries.append(self.allocator, sym); - log.debug(" | found GOT entry dyld_stub_binder: {}", .{self.got_entries.get("dyld_stub_binder")}); + log.debug(" | found GOT entry {s}: {*}", .{ sym.name, sym }); } fn resolveRelocsAndWriteSections(self: *Zld) !void { @@ -1412,9 +1493,14 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { log.debug("relocating object {s}", .{object.name}); for (object.sections.items) |sect, source_sect_id| { + if (sect.inner.flags == macho.S_MOD_INIT_FUNC_POINTERS or + sect.inner.flags == macho.S_MOD_TERM_FUNC_POINTERS) continue; + const segname = parseName(§.inner.segname); const sectname = parseName(§.inner.sectname); + log.debug("relocating section '{s},{s}'", .{ segname, sectname }); + // Get mapping const target_mapping = self.mappings.get(.{ .object_id = @intCast(u16, object_id), @@ -1473,11 +1559,8 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { // TLV is handled via a separate offset mechanism. // Calculate the offset to the initializer. if (target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: { - const sym = object.symtab.items[rel.target.symbol]; - const sym_name = object.getString(sym.n_strx); - // TODO we don't want to save offset to tlv_bootstrap - if (mem.eql(u8, sym_name, "__tlv_bootstrap")) break :tlv; + if (mem.eql(u8, rel.target.symbol.name, "__tlv_bootstrap")) break :tlv; const base_addr = blk: { if (self.tlv_data_section_index) |index| { @@ -1490,16 +1573,17 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { }; // Since we require TLV data to always preceed TLV bss section, we calculate // offsets wrt to the former if it is defined; otherwise, wrt to the latter. - try self.threadlocal_offsets.append(self.allocator, args.target_addr - base_addr); + try self.threadlocal_offsets.append(self.allocator, .{ + .source_addr = args.source_addr, + .offset = args.target_addr - base_addr, + }); } }, .got_page, .got_page_off, .got_load, .got => { const dc_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; const got = dc_seg.sections.items[self.got_section_index.?]; - const sym = object.symtab.items[rel.target.symbol]; - const sym_name = object.getString(sym.n_strx); - const entry = self.got_entries.get(sym_name) orelse unreachable; - args.target_addr = got.addr + entry.index * @sizeOf(u64); + const final = rel.target.symbol.getTopmostAlias(); + args.target_addr = got.addr + final.got_index.? * @sizeOf(u64); }, else => |tt| { if (tt == .signed and rel.target == .section) { @@ -1532,6 +1616,7 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { target_sect_off, target_sect_off + sect.code.len, }); + // Zero-out the space var zeroes = try self.allocator.alloc(u8, sect.code.len); defer self.allocator.free(zeroes); @@ -1545,51 +1630,28 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { } fn relocTargetAddr(self: *Zld, object_id: u16, target: reloc.Relocation.Target) !u64 { - const object = self.objects.items[object_id]; const target_addr = blk: { switch (target) { - .symbol => |sym_id| { - const sym = object.symtab.items[sym_id]; - const sym_name = object.getString(sym.n_strx); - - if (Symbol.isSect(sym)) { - log.debug(" | local symbol '{s}'", .{sym_name}); - if (object.locals.get(sym_name)) |local| { - break :blk local.address; - } - // For temp locals, i.e., symbols prefixed with l... we relocate - // based on section addressing. - const source_sect_id = sym.n_sect - 1; - const target_mapping = self.mappings.get(.{ - .object_id = object_id, - .source_sect_id = source_sect_id, - }) orelse unreachable; - - const source_seg = object.load_commands.items[object.segment_cmd_index.?].Segment; - const source_sect = source_seg.sections.items[source_sect_id]; - const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment; - const target_sect = target_seg.sections.items[target_mapping.target_sect_id]; - const target_addr = target_sect.addr + target_mapping.offset; - break :blk sym.n_value - source_sect.addr + target_addr; - } else { - if (self.stubs.get(sym_name)) |index| { - log.debug(" | symbol stub '{s}'", .{sym_name}); - const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; - const stubs = segment.sections.items[self.stubs_section_index.?]; - break :blk stubs.addr + index * stubs.reserved2; - } else if (mem.eql(u8, sym_name, "__tlv_bootstrap")) { + .symbol => |sym| { + const final = sym.getTopmostAlias(); + if (final.cast(Symbol.Regular)) |reg| { + log.debug(" | regular '{s}'", .{sym.name}); + break :blk reg.address; + } else if (final.cast(Symbol.Proxy)) |proxy| { + if (mem.eql(u8, sym.name, "__tlv_bootstrap")) { log.debug(" | symbol '__tlv_bootstrap'", .{}); const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment; const tlv = segment.sections.items[self.tlv_section_index.?]; break :blk tlv.addr; - } else { - const global = self.symtab.get(sym_name) orelse { - log.err("failed to resolve symbol '{s}' as a relocation target", .{sym_name}); - return error.FailedToResolveRelocationTarget; - }; - log.debug(" | global symbol '{s}'", .{sym_name}); - break :blk global.address; } + + log.debug(" | symbol stub '{s}'", .{sym.name}); + const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; + const stubs = segment.sections.items[self.stubs_section_index.?]; + break :blk stubs.addr + proxy.base.stubs_index.? * stubs.reserved2; + } else { + log.err("failed to resolve symbol '{s}' as a relocation target", .{sym.name}); + return error.FailedToResolveRelocationTarget; } }, .section => |sect_id| { @@ -2008,6 +2070,15 @@ fn populateMetadata(self: *Zld) !void { } fn flush(self: *Zld) !void { + try self.writeStubHelperCommon(); + try self.resolveRelocsAndWriteSections(); + + if (self.common_section_index) |index| { + const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; + const sect = &seg.sections.items[index]; + sect.offset = 0; + } + if (self.bss_section_index) |index| { const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; const sect = &seg.sections.items[index]; @@ -2031,15 +2102,35 @@ fn flush(self: *Zld) !void { var stream = std.io.fixedBufferStream(buffer); var writer = stream.writer(); + std.sort.sort(TlvOffset, self.threadlocal_offsets.items, {}, TlvOffset.cmp); + const seek_amt = 2 * @sizeOf(u64); - while (self.threadlocal_offsets.popOrNull()) |offset| { + for (self.threadlocal_offsets.items) |tlv| { try writer.context.seekBy(seek_amt); - try writer.writeIntLittle(u64, offset); + try writer.writeIntLittle(u64, tlv.offset); } try self.file.?.pwriteAll(buffer, sect.offset); } + if (self.mod_init_func_section_index) |index| { + const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; + const sect = &seg.sections.items[index]; + + var initializers = std.ArrayList(u64).init(self.allocator); + defer initializers.deinit(); + + for (self.objects.items) |object| { + for (object.initializers.items) |initializer| { + const address = initializer.cast(Symbol.Regular).?.address; + try initializers.append(address); + } + } + + _ = try self.file.?.pwriteAll(mem.sliceAsBytes(initializers.items), sect.offset); + sect.size = @intCast(u32, initializers.items.len * @sizeOf(u64)); + } + try self.writeGotEntries(); try self.setEntryPoint(); try self.writeRebaseInfoTable(); @@ -2086,14 +2177,15 @@ fn writeGotEntries(self: *Zld) !void { const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; const sect = seg.sections.items[self.got_section_index.?]; - var buffer = try self.allocator.alloc(u8, self.got_entries.items().len * @sizeOf(u64)); + var buffer = try self.allocator.alloc(u8, self.got_entries.items.len * @sizeOf(u64)); defer self.allocator.free(buffer); var stream = std.io.fixedBufferStream(buffer); var writer = stream.writer(); - for (self.got_entries.items()) |entry| { - try writer.writeIntLittle(u64, entry.value.target_addr); + for (self.got_entries.items) |sym| { + const address: u64 = if (sym.cast(Symbol.Regular)) |reg| reg.address else 0; + try writer.writeIntLittle(u64, address); } log.debug("writing GOT pointers at 0x{x} to 0x{x}", .{ sect.offset, sect.offset + buffer.len }); @@ -2106,7 +2198,8 @@ fn setEntryPoint(self: *Zld) !void { // entrypoint. For now, assume default of `_main`. const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; const text = seg.sections.items[self.text_section_index.?]; - const entry_sym = self.symtab.get("_main") orelse return error.MissingMainEntrypoint; + const sym = self.globals.get("_main") orelse return error.MissingMainEntrypoint; + const entry_sym = sym.cast(Symbol.Regular) orelse unreachable; const ec = &self.load_commands.items[self.main_cmd_index.?].Main; ec.entryoff = @intCast(u32, entry_sym.address - seg.inner.vmaddr); } @@ -2119,55 +2212,35 @@ fn writeRebaseInfoTable(self: *Zld) !void { pointers.appendSliceAssumeCapacity(self.local_rebases.items); if (self.got_section_index) |idx| { - // TODO this should be cleaned up! const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; const sect = seg.sections.items[idx]; const base_offset = sect.addr - seg.inner.vmaddr; const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); - for (self.got_entries.items()) |entry| { - if (entry.value.tag == .import) continue; - + for (self.got_entries.items) |sym| { + if (sym.@"type" == .proxy) continue; try pointers.append(.{ - .offset = base_offset + entry.value.index * @sizeOf(u64), + .offset = base_offset + sym.got_index.? * @sizeOf(u64), .segment_id = segment_id, }); } } if (self.mod_init_func_section_index) |idx| { - // TODO audit and investigate this. - const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; - const sect = seg.sections.items[idx]; - const npointers = sect.size * @sizeOf(u64); - const base_offset = sect.addr - seg.inner.vmaddr; - const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); - - try pointers.ensureCapacity(pointers.items.len + npointers); - var i: usize = 0; - while (i < npointers) : (i += 1) { - pointers.appendAssumeCapacity(.{ - .offset = base_offset + i * @sizeOf(u64), - .segment_id = segment_id, - }); - } - } - - if (self.mod_term_func_section_index) |idx| { - // TODO audit and investigate this. const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; const sect = seg.sections.items[idx]; - const npointers = sect.size * @sizeOf(u64); const base_offset = sect.addr - seg.inner.vmaddr; const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); - try pointers.ensureCapacity(pointers.items.len + npointers); - var i: usize = 0; - while (i < npointers) : (i += 1) { - pointers.appendAssumeCapacity(.{ - .offset = base_offset + i * @sizeOf(u64), - .segment_id = segment_id, - }); + var index: u64 = 0; + for (self.objects.items) |object| { + for (object.initializers.items) |_| { + try pointers.append(.{ + .offset = base_offset + index * @sizeOf(u64), + .segment_id = segment_id, + }); + index += 1; + } } } @@ -2177,10 +2250,10 @@ fn writeRebaseInfoTable(self: *Zld) !void { const base_offset = sect.addr - seg.inner.vmaddr; const segment_id = @intCast(u16, self.data_segment_cmd_index.?); - try pointers.ensureCapacity(pointers.items.len + self.stubs.items().len); - for (self.stubs.items()) |entry| { + try pointers.ensureCapacity(pointers.items.len + self.stubs.items.len); + for (self.stubs.items) |sym| { pointers.appendAssumeCapacity(.{ - .offset = base_offset + entry.value * @sizeOf(u64), + .offset = base_offset + sym.stubs_index.? * @sizeOf(u64), .segment_id = segment_id, }); } @@ -2216,21 +2289,16 @@ fn writeBindInfoTable(self: *Zld) !void { const base_offset = sect.addr - seg.inner.vmaddr; const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); - for (self.got_entries.items()) |entry| { - if (entry.value.tag == .local) continue; - - const dylib_ordinal = dylib_ordinal: { - const sym = self.symtab.get(entry.key) orelse continue; // local indirection - if (sym.tag != .import) continue; // local indirection - break :dylib_ordinal sym.file.? + 1; - }; - - try pointers.append(.{ - .offset = base_offset + entry.value.index * @sizeOf(u64), - .segment_id = segment_id, - .dylib_ordinal = dylib_ordinal, - .name = entry.key, - }); + for (self.got_entries.items) |sym| { + if (sym.cast(Symbol.Proxy)) |proxy| { + const dylib_ordinal = proxy.dylib + 1; + try pointers.append(.{ + .offset = base_offset + proxy.base.got_index.? * @sizeOf(u64), + .segment_id = segment_id, + .dylib_ordinal = dylib_ordinal, + .name = proxy.base.name, + }); + } } } @@ -2240,14 +2308,15 @@ fn writeBindInfoTable(self: *Zld) !void { const base_offset = sect.addr - seg.inner.vmaddr; const segment_id = @intCast(u16, self.data_segment_cmd_index.?); - const sym = self.symtab.get("__tlv_bootstrap") orelse unreachable; - const dylib_ordinal = sym.file.? + 1; + const sym = self.imports.get("__tlv_bootstrap") orelse unreachable; + const proxy = sym.cast(Symbol.Proxy) orelse unreachable; + const dylib_ordinal = proxy.dylib + 1; try pointers.append(.{ .offset = base_offset, .segment_id = segment_id, .dylib_ordinal = dylib_ordinal, - .name = "__tlv_bootstrap", + .name = proxy.base.name, }); } @@ -2279,20 +2348,16 @@ fn writeLazyBindInfoTable(self: *Zld) !void { const base_offset = sect.addr - seg.inner.vmaddr; const segment_id = @intCast(u16, self.data_segment_cmd_index.?); - try pointers.ensureCapacity(self.stubs.items().len); - - for (self.stubs.items()) |entry| { - const dylib_ordinal = dylib_ordinal: { - const sym = self.symtab.get(entry.key) orelse unreachable; - assert(sym.tag == .import); - break :dylib_ordinal sym.file.? + 1; - }; + try pointers.ensureCapacity(self.stubs.items.len); + for (self.stubs.items) |sym| { + const proxy = sym.cast(Symbol.Proxy) orelse unreachable; + const dylib_ordinal = proxy.dylib + 1; pointers.appendAssumeCapacity(.{ - .offset = base_offset + entry.value * @sizeOf(u64), + .offset = base_offset + sym.stubs_index.? * @sizeOf(u64), .segment_id = segment_id, .dylib_ordinal = dylib_ordinal, - .name = entry.key, + .name = sym.name, }); } } @@ -2361,7 +2426,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void { else => {}, } } - assert(self.stubs.items().len <= offsets.items.len); + assert(self.stubs.items.len <= offsets.items.len); const stub_size: u4 = switch (self.arch.?) { .x86_64 => 10, @@ -2374,9 +2439,10 @@ fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void { else => unreachable, }; var buf: [@sizeOf(u32)]u8 = undefined; - for (self.stubs.items()) |entry| { - const placeholder_off = self.stub_helper_stubs_start_off.? + entry.value * stub_size + off; - mem.writeIntLittle(u32, &buf, offsets.items[entry.value]); + for (self.stubs.items) |sym| { + const index = sym.stubs_index orelse unreachable; + const placeholder_off = self.stub_helper_stubs_start_off.? + index * stub_size + off; + mem.writeIntLittle(u32, &buf, offsets.items[index]); try self.file.?.pwriteAll(&buf, placeholder_off); } } @@ -2388,12 +2454,13 @@ fn writeExportInfo(self: *Zld) !void { const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; // TODO export items for dylibs - const sym = self.symtab.get("_main") orelse return error.MissingMainEntrypoint; - assert(sym.address >= text_segment.inner.vmaddr); + const sym = self.globals.get("_main") orelse return error.MissingMainEntrypoint; + const reg = sym.cast(Symbol.Regular) orelse unreachable; + assert(reg.address >= text_segment.inner.vmaddr); try trie.put(.{ - .name = "_main", - .vmaddr_offset = sym.address - text_segment.inner.vmaddr, + .name = sym.name, + .vmaddr_offset = reg.address - text_segment.inner.vmaddr, .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR, }); @@ -2421,7 +2488,7 @@ fn writeDebugInfo(self: *Zld) !void { var stabs = std.ArrayList(macho.nlist_64).init(self.allocator); defer stabs.deinit(); - for (self.objects.items) |object, object_id| { + for (self.objects.items) |object| { const tu_path = object.tu_path orelse continue; const tu_mtime = object.tu_mtime orelse continue; const dirname = std.fs.path.dirname(tu_path) orelse "./"; @@ -2447,42 +2514,45 @@ fn writeDebugInfo(self: *Zld) !void { .n_type = macho.N_OSO, .n_sect = 0, .n_desc = 1, - .n_value = tu_mtime, + .n_value = 0, //tu_mtime, TODO figure out why precalculated mtime value doesn't work }); - for (object.stabs.items) |stab| { - const entry = object.locals.items()[stab.symbol]; - const sym = entry.value; + for (object.symbols.items) |sym| { + if (sym.@"type" != .regular) continue; + const reg = sym.cast(Symbol.Regular) orelse unreachable; + + if (reg.isTemp() or reg.stab == null) continue; + const stab = reg.stab orelse unreachable; - switch (stab.tag) { + switch (stab.kind) { .function => { try stabs.append(.{ .n_strx = 0, .n_type = macho.N_BNSYM, - .n_sect = sym.section, + .n_sect = reg.section, .n_desc = 0, - .n_value = sym.address, + .n_value = reg.address, }); try stabs.append(.{ .n_strx = try self.makeString(sym.name), .n_type = macho.N_FUN, - .n_sect = sym.section, + .n_sect = reg.section, .n_desc = 0, - .n_value = sym.address, + .n_value = reg.address, }); try stabs.append(.{ .n_strx = 0, .n_type = macho.N_FUN, .n_sect = 0, .n_desc = 0, - .n_value = stab.size.?, + .n_value = stab.size, }); try stabs.append(.{ .n_strx = 0, .n_type = macho.N_ENSYM, - .n_sect = sym.section, + .n_sect = reg.section, .n_desc = 0, - .n_value = stab.size.?, + .n_value = stab.size, }); }, .global => { @@ -2498,9 +2568,9 @@ fn writeDebugInfo(self: *Zld) !void { try stabs.append(.{ .n_strx = try self.makeString(sym.name), .n_type = macho.N_STSYM, - .n_sect = sym.section, + .n_sect = reg.section, .n_desc = 0, - .n_value = sym.address, + .n_value = reg.address, }); }, } @@ -2536,27 +2606,6 @@ fn writeDebugInfo(self: *Zld) !void { dysymtab.nlocalsym = symtab.nsyms; } -fn populateStringTable(self: *Zld) !void { - for (self.objects.items) |*object| { - for (object.symtab.items) |*sym| { - switch (sym.tag) { - .undef, .import => continue, - else => {}, - } - const sym_name = object.getString(sym.inner.n_strx); - const n_strx = try self.makeString(sym_name); - sym.inner.n_strx = n_strx; - } - } - - for (self.symtab.items()) |*entry| { - if (entry.value.tag != .import) continue; - - const n_strx = try self.makeString(entry.key); - entry.value.inner.n_strx = n_strx; - } -} - fn writeSymbolTable(self: *Zld) !void { const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; @@ -2564,56 +2613,52 @@ fn writeSymbolTable(self: *Zld) !void { var locals = std.ArrayList(macho.nlist_64).init(self.allocator); defer locals.deinit(); + var exports = std.ArrayList(macho.nlist_64).init(self.allocator); + defer exports.deinit(); + for (self.objects.items) |object| { - for (object.locals.items()) |entry| { - const sym = entry.value; - if (sym.tag != .local) continue; - - try locals.append(.{ - .n_strx = try self.makeString(sym.name), - .n_type = macho.N_SECT, - .n_sect = sym.section, - .n_desc = 0, - .n_value = sym.address, - }); + for (object.symbols.items) |sym| { + const final = sym.getTopmostAlias(); + if (final.@"type" != .regular) continue; + + const reg = final.cast(Symbol.Regular) orelse unreachable; + if (reg.isTemp()) continue; + + switch (reg.linkage) { + .translation_unit => { + try locals.append(.{ + .n_strx = try self.makeString(sym.name), + .n_type = macho.N_SECT, + .n_sect = reg.section, + .n_desc = 0, + .n_value = reg.address, + }); + }, + else => { + try exports.append(.{ + .n_strx = try self.makeString(sym.name), + .n_type = macho.N_SECT | macho.N_EXT, + .n_sect = reg.section, + .n_desc = 0, + .n_value = reg.address, + }); + }, + } } } - var exports = std.ArrayList(macho.nlist_64).init(self.allocator); - defer exports.deinit(); - var undefs = std.ArrayList(macho.nlist_64).init(self.allocator); defer undefs.deinit(); - var undefs_ids = std.StringHashMap(u32).init(self.allocator); - defer undefs_ids.deinit(); - - var undef_id: u32 = 0; - for (self.symtab.items()) |entry| { + for (self.imports.items()) |entry| { const sym = entry.value; - switch (sym.tag) { - .weak, .strong => { - try exports.append(.{ - .n_strx = try self.makeString(sym.name), - .n_type = macho.N_SECT | macho.N_EXT, - .n_sect = sym.section, - .n_desc = 0, - .n_value = sym.address, - }); - }, - .import => { - try undefs.append(.{ - .n_strx = try self.makeString(sym.name), - .n_type = macho.N_UNDF | macho.N_EXT, - .n_sect = 0, - .n_desc = macho.N_SYMBOL_RESOLVER | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY, - .n_value = 0, - }); - try undefs_ids.putNoClobber(sym.name, undef_id); - undef_id += 1; - }, - else => unreachable, - } + try undefs.append(.{ + .n_strx = try self.makeString(sym.name), + .n_type = macho.N_UNDF | macho.N_EXT, + .n_sect = 0, + .n_desc = macho.N_SYMBOL_RESOLVER | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY, + .n_value = 0, + }); } const nlocals = locals.items.len; @@ -2653,8 +2698,8 @@ fn writeSymbolTable(self: *Zld) !void { const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?]; - const nstubs = @intCast(u32, self.stubs.items().len); - const ngot_entries = @intCast(u32, self.got_entries.items().len); + const nstubs = @intCast(u32, self.stubs.items.len); + const ngot_entries = @intCast(u32, self.got_entries.items.len); dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); dysymtab.nindirectsyms = nstubs * 2 + ngot_entries; @@ -2674,25 +2719,25 @@ fn writeSymbolTable(self: *Zld) !void { var writer = stream.writer(); stubs.reserved1 = 0; - for (self.stubs.items()) |entry| { - const id = undefs_ids.get(entry.key) orelse unreachable; - try writer.writeIntLittle(u32, dysymtab.iundefsym + id); + for (self.stubs.items) |sym| { + const id = self.imports.getIndex(sym.name) orelse unreachable; + try writer.writeIntLittle(u32, dysymtab.iundefsym + @intCast(u32, id)); } got.reserved1 = nstubs; - for (self.got_entries.items()) |entry| { - if (entry.value.tag == .import) { - const id = undefs_ids.get(entry.key) orelse unreachable; - try writer.writeIntLittle(u32, dysymtab.iundefsym + id); + for (self.got_entries.items) |sym| { + if (sym.@"type" == .proxy) { + const id = self.imports.getIndex(sym.name) orelse unreachable; + try writer.writeIntLittle(u32, dysymtab.iundefsym + @intCast(u32, id)); } else { try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL); } } la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries; - for (self.stubs.items()) |entry| { - const id = undefs_ids.get(entry.key) orelse unreachable; - try writer.writeIntLittle(u32, dysymtab.iundefsym + id); + for (self.stubs.items) |sym| { + const id = self.imports.getIndex(sym.name) orelse unreachable; + try writer.writeIntLittle(u32, dysymtab.iundefsym + @intCast(u32, id)); } try self.file.?.pwriteAll(buf, dysymtab.indirectsymoff); @@ -2889,3 +2934,33 @@ pub fn parseName(name: *const [16]u8) []const u8 { const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len; return name[0..len]; } + +fn printSymbols(self: *Zld) void { + log.debug("globals", .{}); + for (self.globals.items()) |entry| { + const sym = entry.value.cast(Symbol.Regular) orelse unreachable; + log.debug(" | {s} @ {*}", .{ sym.base.name, entry.value }); + log.debug(" => alias of {*}", .{sym.base.alias}); + log.debug(" => linkage {s}", .{sym.linkage}); + log.debug(" => defined in {s}", .{sym.file.name.?}); + } + for (self.objects.items) |object| { + log.debug("locals in {s}", .{object.name.?}); + for (object.symbols.items) |sym| { + log.debug(" | {s} @ {*}", .{ sym.name, sym }); + log.debug(" => alias of {*}", .{sym.alias}); + if (sym.cast(Symbol.Regular)) |reg| { + log.debug(" => linkage {s}", .{reg.linkage}); + } else { + log.debug(" => unresolved", .{}); + } + } + } + log.debug("proxies", .{}); + for (self.imports.items()) |entry| { + const sym = entry.value.cast(Symbol.Proxy) orelse unreachable; + log.debug(" | {s} @ {*}", .{ sym.base.name, entry.value }); + log.debug(" => alias of {*}", .{sym.base.alias}); + log.debug(" => defined in libSystem.B.dylib", .{}); + } +} diff --git a/zig/src/link/MachO/reloc.zig b/zig/src/link/MachO/reloc.zig index 57825149d1..1ce9fa2c2d 100644 --- a/zig/src/link/MachO/reloc.zig +++ b/zig/src/link/MachO/reloc.zig @@ -10,6 +10,7 @@ const aarch64 = @import("reloc/aarch64.zig"); const x86_64 = @import("reloc/x86_64.zig"); const Allocator = mem.Allocator; +const Symbol = @import("Symbol.zig"); pub const Relocation = struct { @"type": Type, @@ -75,12 +76,12 @@ pub const Relocation = struct { }; pub const Target = union(enum) { - symbol: u32, + symbol: *Symbol, section: u16, - pub fn from_reloc(reloc: macho.relocation_info) Target { + pub fn from_reloc(reloc: macho.relocation_info, symbols: []*Symbol) Target { return if (reloc.r_extern == 1) .{ - .symbol = reloc.r_symbolnum, + .symbol = symbols[reloc.r_symbolnum], } else .{ .section = @intCast(u16, reloc.r_symbolnum - 1), }; @@ -136,6 +137,7 @@ pub fn parse( arch: std.Target.Cpu.Arch, code: []u8, relocs: []const macho.relocation_info, + symbols: []*Symbol, ) ![]*Relocation { var it = RelocIterator{ .buffer = relocs, @@ -148,6 +150,7 @@ pub fn parse( .it = &it, .code = code, .parsed = std.ArrayList(*Relocation).init(allocator), + .symbols = symbols, }; defer parser.deinit(); try parser.parse(); @@ -160,6 +163,7 @@ pub fn parse( .it = &it, .code = code, .parsed = std.ArrayList(*Relocation).init(allocator), + .symbols = symbols, }; defer parser.deinit(); try parser.parse(); diff --git a/zig/src/link/MachO/reloc/aarch64.zig b/zig/src/link/MachO/reloc/aarch64.zig index a7dd0919b4..c08934d84b 100644 --- a/zig/src/link/MachO/reloc/aarch64.zig +++ b/zig/src/link/MachO/reloc/aarch64.zig @@ -10,6 +10,7 @@ const reloc = @import("../reloc.zig"); const Allocator = mem.Allocator; const Relocation = reloc.Relocation; +const Symbol = @import("../Symbol.zig"); pub const Branch = struct { base: Relocation, @@ -24,7 +25,7 @@ pub const Branch = struct { log.debug(" | displacement 0x{x}", .{displacement}); var inst = branch.inst; - inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement) >> 2); + inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2)); mem.writeIntLittle(u32, branch.base.code[0..4], inst.toU32()); } }; @@ -188,6 +189,7 @@ pub const Parser = struct { it: *reloc.RelocIterator, code: []u8, parsed: std.ArrayList(*Relocation), + symbols: []*Symbol, addend: ?u32 = null, subtractor: ?Relocation.Target = null, @@ -226,7 +228,9 @@ pub const Parser = struct { try parser.parseTlvpLoadPageOff(rel); }, .ARM64_RELOC_POINTER_TO_GOT => { - return error.ToDoRelocPointerToGot; + // TODO Handle pointer to GOT. This reloc seems to appear in + // __LD,__compact_unwind section which we currently don't handle. + log.debug("Unhandled relocation ARM64_RELOC_POINTER_TO_GOT", .{}); }, } } @@ -271,7 +275,7 @@ pub const Parser = struct { var branch = try parser.allocator.create(Branch); errdefer parser.allocator.destroy(branch); - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); branch.* = .{ .base = .{ @@ -292,7 +296,7 @@ pub const Parser = struct { assert(rel.r_length == 2); const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); const offset = @intCast(u32, rel.r_address); const inst = parser.code[offset..][0..4]; @@ -398,7 +402,7 @@ pub const Parser = struct { aarch64.Instruction.load_store_register, ), inst) }; } - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); var page_off = try parser.allocator.create(PageOff); errdefer parser.allocator.destroy(page_off); @@ -435,7 +439,7 @@ pub const Parser = struct { ), inst); assert(parsed_inst.size == 3); - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); var page_off = try parser.allocator.create(GotPageOff); errdefer parser.allocator.destroy(page_off); @@ -494,7 +498,7 @@ pub const Parser = struct { } }; - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); var page_off = try parser.allocator.create(TlvpPageOff); errdefer parser.allocator.destroy(page_off); @@ -529,7 +533,7 @@ pub const Parser = struct { assert(rel.r_pcrel == 0); assert(parser.subtractor == null); - parser.subtractor = Relocation.Target.from_reloc(rel); + parser.subtractor = Relocation.Target.from_reloc(rel, parser.symbols); // Verify SUBTRACTOR is followed by UNSIGNED. const next = @intToEnum(macho.reloc_type_arm64, parser.it.peek().r_type); @@ -552,7 +556,7 @@ pub const Parser = struct { var unsigned = try parser.allocator.create(reloc.Unsigned); errdefer parser.allocator.destroy(unsigned); - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); const is_64bit: bool = switch (rel.r_length) { 3 => true, 2 => false, diff --git a/zig/src/link/MachO/reloc/x86_64.zig b/zig/src/link/MachO/reloc/x86_64.zig index cdc90aac90..32f83924e8 100644 --- a/zig/src/link/MachO/reloc/x86_64.zig +++ b/zig/src/link/MachO/reloc/x86_64.zig @@ -9,6 +9,7 @@ const reloc = @import("../reloc.zig"); const Allocator = mem.Allocator; const Relocation = reloc.Relocation; +const Symbol = @import("../Symbol.zig"); pub const Branch = struct { base: Relocation, @@ -95,6 +96,7 @@ pub const Parser = struct { it: *reloc.RelocIterator, code: []u8, parsed: std.ArrayList(*Relocation), + symbols: []*Symbol, subtractor: ?Relocation.Target = null, pub fn deinit(parser: *Parser) void { @@ -145,7 +147,7 @@ pub const Parser = struct { var branch = try parser.allocator.create(Branch); errdefer parser.allocator.destroy(branch); - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); branch.* = .{ .base = .{ @@ -165,7 +167,7 @@ pub const Parser = struct { assert(rel.r_length == 2); const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type); - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); const is_extern = rel.r_extern == 1; const offset = @intCast(u32, rel.r_address); @@ -211,7 +213,7 @@ pub const Parser = struct { const offset = @intCast(u32, rel.r_address); const inst = parser.code[offset..][0..4]; - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); var got_load = try parser.allocator.create(GotLoad); errdefer parser.allocator.destroy(got_load); @@ -237,7 +239,7 @@ pub const Parser = struct { const offset = @intCast(u32, rel.r_address); const inst = parser.code[offset..][0..4]; - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); var got = try parser.allocator.create(Got); errdefer parser.allocator.destroy(got); @@ -263,7 +265,7 @@ pub const Parser = struct { const offset = @intCast(u32, rel.r_address); const inst = parser.code[offset..][0..4]; - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); var tlv = try parser.allocator.create(Tlv); errdefer parser.allocator.destroy(tlv); @@ -288,7 +290,7 @@ pub const Parser = struct { assert(rel.r_pcrel == 0); assert(parser.subtractor == null); - parser.subtractor = Relocation.Target.from_reloc(rel); + parser.subtractor = Relocation.Target.from_reloc(rel, parser.symbols); // Verify SUBTRACTOR is followed by UNSIGNED. const next = @intToEnum(macho.reloc_type_x86_64, parser.it.peek().r_type); @@ -311,7 +313,7 @@ pub const Parser = struct { var unsigned = try parser.allocator.create(reloc.Unsigned); errdefer parser.allocator.destroy(unsigned); - const target = Relocation.Target.from_reloc(rel); + const target = Relocation.Target.from_reloc(rel, parser.symbols); const is_64bit: bool = switch (rel.r_length) { 3 => true, 2 => false, diff --git a/zig/src/main.zig b/zig/src/main.zig index e5470a8cca..a0a11bbbbc 100644 --- a/zig/src/main.zig +++ b/zig/src/main.zig @@ -340,6 +340,8 @@ const usage_build_generic = \\ -rpath [path] Add directory to the runtime library search path \\ -feach-lib-rpath Ensure adding rpath for each used dynamic library \\ -fno-each-lib-rpath Prevent adding rpath for each used dynamic library + \\ -fallow-shlib-undefined Allows undefined symbols in shared libraries + \\ -fno-allow-shlib-undefined Disallows undefined symbols in shared libraries \\ --eh-frame-hdr Enable C++ exception handling by passing --eh-frame-hdr to linker \\ --emit-relocs Enable output of relocation sections for post build tools \\ -dynamic Force output to be dynamically linked @@ -670,7 +672,7 @@ fn buildOutputType( extra_cflags.shrinkRetainingCapacity(0); while (true) { i += 1; - if (i + 1 >= args.len) fatal("expected -- after -cflags", .{}); + if (i >= args.len) fatal("expected -- after -cflags", .{}); if (mem.eql(u8, args[i], "--")) break; try extra_cflags.append(args[i]); } @@ -973,6 +975,10 @@ fn buildOutputType( link_eh_frame_hdr = true; } else if (mem.eql(u8, arg, "--emit-relocs")) { link_emit_relocs = true; + } else if (mem.eql(u8, arg, "-fallow-shlib-undefined")) { + linker_allow_shlib_undefined = true; + } else if (mem.eql(u8, arg, "-fno-allow-shlib-undefined")) { + linker_allow_shlib_undefined = false; } else if (mem.eql(u8, arg, "-Bsymbolic")) { linker_bind_global_refs_locally = true; } else if (mem.eql(u8, arg, "--verbose-link")) { @@ -2623,7 +2629,10 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v }; defer comp.destroy(); - try updateModule(gpa, comp, .none); + updateModule(gpa, comp, .none) catch |err| switch (err) { + error.SemanticAnalyzeFail => process.exit(1), + else => |e| return e, + }; try comp.makeBinFileExecutable(); child_argv.items[argv_index_exe] = try comp.bin_file.options.emit.?.directory.join( @@ -2859,6 +2868,7 @@ const FmtError = error{ Unseekable, NotOpenForWriting, UnsupportedEncoding, + ConnectionResetByPeer, } || fs.File.OpenError; fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void { diff --git a/zig/src/register_manager.zig b/zig/src/register_manager.zig index e11f2c3111..270c762887 100644 --- a/zig/src/register_manager.zig +++ b/zig/src/register_manager.zig @@ -16,7 +16,7 @@ pub fn RegisterManager( ) type { return struct { /// The key must be canonical register. - registers: std.AutoHashMapUnmanaged(Register, *ir.Inst) = .{}, + registers: [callee_preserved_regs.len]?*ir.Inst = [_]?*ir.Inst{null} ** callee_preserved_regs.len, free_registers: FreeRegInt = math.maxInt(FreeRegInt), /// Tracks all registers allocated in the course of this function allocated_registers: FreeRegInt = 0, @@ -31,14 +31,6 @@ pub fn RegisterManager( return @fieldParentPtr(Function, "register_manager", self); } - pub fn deinit(self: *Self, allocator: *Allocator) void { - self.registers.deinit(allocator); - } - - fn isTracked(reg: Register) bool { - return std.mem.indexOfScalar(Register, callee_preserved_regs, reg) != null; - } - fn markRegUsed(self: *Self, reg: Register) void { if (FreeRegInt == u0) return; const index = reg.allocIndex() orelse return; @@ -55,6 +47,7 @@ pub fn RegisterManager( self.free_registers |= @as(FreeRegInt, 1) << shift; } + /// Returns true when this register is not tracked pub fn isRegFree(self: Self, reg: Register) bool { if (FreeRegInt == u0) return true; const index = reg.allocIndex() orelse return true; @@ -63,7 +56,8 @@ pub fn RegisterManager( } /// Returns whether this register was allocated in the course - /// of this function + /// of this function. + /// Returns false when this register is not tracked pub fn isRegAllocated(self: Self, reg: Register) bool { if (FreeRegInt == u0) return false; const index = reg.allocIndex() orelse return false; @@ -71,82 +65,121 @@ pub fn RegisterManager( return self.allocated_registers & @as(FreeRegInt, 1) << shift != 0; } - /// Before calling, must ensureCapacity + 1 on self.registers. /// Returns `null` if all registers are allocated. - pub fn tryAllocReg(self: *Self, inst: *ir.Inst) ?Register { - const free_index = @ctz(FreeRegInt, self.free_registers); - if (free_index >= callee_preserved_regs.len) { + pub fn tryAllocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ?[count]Register { + if (self.tryAllocRegsWithoutTracking(count)) |regs| { + for (regs) |reg, i| { + const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null + self.registers[index] = insts[i]; + self.markRegUsed(reg); + } + + return regs; + } else { return null; } + } - // This is necessary because the return type of @ctz is 1 - // bit longer than ShiftInt if callee_preserved_regs.len - // is a power of two. This int cast is always safe because - // free_index < callee_preserved_regs.len - const shift = @intCast(ShiftInt, free_index); - const mask = @as(FreeRegInt, 1) << shift; - self.free_registers &= ~mask; - self.allocated_registers |= mask; + /// Returns `null` if all registers are allocated. + pub fn tryAllocReg(self: *Self, inst: *ir.Inst) ?Register { + return if (tryAllocRegs(self, 1, .{inst})) |regs| regs[0] else null; + } - const reg = callee_preserved_regs[free_index]; - self.registers.putAssumeCapacityNoClobber(reg, inst); - log.debug("alloc {} => {*}", .{ reg, inst }); - return reg; + pub fn allocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ![count]Register { + comptime assert(count > 0 and count <= callee_preserved_regs.len); + + return self.tryAllocRegs(count, insts) orelse blk: { + // We'll take over the first count registers. Spill + // the instructions that were previously there to a + // stack allocations. + var regs: [count]Register = undefined; + std.mem.copy(Register, ®s, callee_preserved_regs[0..count]); + + for (regs) |reg, i| { + const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null + if (self.isRegFree(reg)) { + self.markRegUsed(reg); + } else { + const spilled_inst = self.registers[index].?; + try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); + } + self.registers[index] = insts[i]; + } + + break :blk regs; + }; } - /// Before calling, must ensureCapacity + 1 on self.registers. pub fn allocReg(self: *Self, inst: *ir.Inst) !Register { - return self.tryAllocReg(inst) orelse b: { - // We'll take over the first register. Move the instruction that was previously - // there to a stack allocation. - const reg = callee_preserved_regs[0]; - const regs_entry = self.registers.getEntry(reg).?; - const spilled_inst = regs_entry.value; - regs_entry.value = inst; - try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); + return (try self.allocRegs(1, .{inst}))[0]; + } - break :b reg; - }; + /// Does not track the registers. + /// Returns `null` if not enough registers are free. + pub fn tryAllocRegsWithoutTracking(self: *Self, comptime count: comptime_int) ?[count]Register { + comptime if (callee_preserved_regs.len == 0) return null; + comptime assert(count > 0 and count <= callee_preserved_regs.len); + + const free_registers = @popCount(FreeRegInt, self.free_registers); + if (free_registers < count) return null; + + var regs: [count]Register = undefined; + var i: usize = 0; + for (callee_preserved_regs) |reg| { + if (i >= count) break; + if (self.isRegFree(reg)) { + regs[i] = reg; + i += 1; + } + } + return regs; } /// Does not track the register. /// Returns `null` if all registers are allocated. - pub fn findUnusedReg(self: *Self) ?Register { - const free_index = @ctz(FreeRegInt, self.free_registers); - if (free_index >= callee_preserved_regs.len) { - return null; - } - return callee_preserved_regs[free_index]; + pub fn tryAllocRegWithoutTracking(self: *Self) ?Register { + return if (self.tryAllocRegsWithoutTracking(1)) |regs| regs[0] else null; + } + + /// Does not track the registers + pub fn allocRegsWithoutTracking(self: *Self, comptime count: comptime_int) ![count]Register { + return self.tryAllocRegsWithoutTracking(count) orelse blk: { + // We'll take over the first count registers. Spill + // the instructions that were previously there to a + // stack allocations. + var regs: [count]Register = undefined; + std.mem.copy(Register, ®s, callee_preserved_regs[0..count]); + + for (regs) |reg, i| { + const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null + if (!self.isRegFree(reg)) { + const spilled_inst = self.registers[index].?; + try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); + self.registers[index] = null; + self.markRegFree(reg); + } + } + + break :blk regs; + }; } /// Does not track the register. pub fn allocRegWithoutTracking(self: *Self) !Register { - return self.findUnusedReg() orelse b: { - // We'll take over the first register. Move the instruction that was previously - // there to a stack allocation. - const reg = callee_preserved_regs[0]; - const regs_entry = self.registers.remove(reg).?; - const spilled_inst = regs_entry.value; - try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); - self.markRegFree(reg); - - break :b reg; - }; + return (try self.allocRegsWithoutTracking(1))[0]; } /// Allocates the specified register with the specified /// instruction. Spills the register if it is currently /// allocated. - /// Before calling, must ensureCapacity + 1 on self.registers. pub fn getReg(self: *Self, reg: Register, inst: *ir.Inst) !void { - if (!isTracked(reg)) return; + const index = reg.allocIndex() orelse return; if (!self.isRegFree(reg)) { // Move the instruction that was previously there to a // stack allocation. - const regs_entry = self.registers.getEntry(reg).?; - const spilled_inst = regs_entry.value; - regs_entry.value = inst; + const spilled_inst = self.registers[index].?; + self.registers[index] = inst; try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); } else { self.getRegAssumeFree(reg, inst); @@ -156,41 +189,43 @@ pub fn RegisterManager( /// Spills the register if it is currently allocated. /// Does not track the register. pub fn getRegWithoutTracking(self: *Self, reg: Register) !void { - if (!isTracked(reg)) return; + const index = reg.allocIndex() orelse return; if (!self.isRegFree(reg)) { // Move the instruction that was previously there to a // stack allocation. - const regs_entry = self.registers.remove(reg).?; - const spilled_inst = regs_entry.value; + const spilled_inst = self.registers[index].?; try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); self.markRegFree(reg); } } /// Allocates the specified register with the specified - /// instruction. Assumes that the register is free and no + /// instruction. Asserts that the register is free and no /// spilling is necessary. - /// Before calling, must ensureCapacity + 1 on self.registers. pub fn getRegAssumeFree(self: *Self, reg: Register, inst: *ir.Inst) void { - if (!isTracked(reg)) return; + const index = reg.allocIndex() orelse return; - self.registers.putAssumeCapacityNoClobber(reg, inst); + assert(self.registers[index] == null); + self.registers[index] = inst; self.markRegUsed(reg); } /// Marks the specified register as free pub fn freeReg(self: *Self, reg: Register) void { - if (!isTracked(reg)) return; + const index = reg.allocIndex() orelse return; - _ = self.registers.remove(reg); + self.registers[index] = null; self.markRegFree(reg); } }; } const MockRegister = enum(u2) { - r0, r1, r2, r3, + r0, + r1, + r2, + r3, pub fn allocIndex(self: MockRegister) ?u2 { inline for (mock_callee_preserved_regs) |cpreg, i| { @@ -210,10 +245,9 @@ const MockFunction = struct { const Self = @This(); pub fn deinit(self: *Self) void { - self.register_manager.deinit(self.allocator); self.spilled.deinit(self.allocator); } - + pub fn spillInstruction(self: *Self, src: LazySrcLoc, reg: MockRegister, inst: *ir.Inst) !void { try self.spilled.append(self.allocator, reg); } @@ -236,7 +270,6 @@ test "tryAllocReg: no spilling" { std.testing.expect(!function.register_manager.isRegAllocated(.r2)); std.testing.expect(!function.register_manager.isRegAllocated(.r3)); - try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2); std.testing.expectEqual(@as(?MockRegister, .r2), function.register_manager.tryAllocReg(&mock_instruction)); std.testing.expectEqual(@as(?MockRegister, .r3), function.register_manager.tryAllocReg(&mock_instruction)); std.testing.expectEqual(@as(?MockRegister, null), function.register_manager.tryAllocReg(&mock_instruction)); @@ -268,7 +301,6 @@ test "allocReg: spilling" { std.testing.expect(!function.register_manager.isRegAllocated(.r2)); std.testing.expect(!function.register_manager.isRegAllocated(.r3)); - try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2); std.testing.expectEqual(@as(?MockRegister, .r2), try function.register_manager.allocReg(&mock_instruction)); std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction)); @@ -299,14 +331,12 @@ test "getReg" { std.testing.expect(!function.register_manager.isRegAllocated(.r2)); std.testing.expect(!function.register_manager.isRegAllocated(.r3)); - try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2); try function.register_manager.getReg(.r3, &mock_instruction); std.testing.expect(!function.register_manager.isRegAllocated(.r2)); std.testing.expect(function.register_manager.isRegAllocated(.r3)); // Spill r3 - try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2); try function.register_manager.getReg(.r3, &mock_instruction); std.testing.expect(!function.register_manager.isRegAllocated(.r2)); diff --git a/zig/src/stage1/all_types.hpp b/zig/src/stage1/all_types.hpp index 6cd28f4bbc..0991faf815 100644 --- a/zig/src/stage1/all_types.hpp +++ b/zig/src/stage1/all_types.hpp @@ -84,6 +84,7 @@ enum CallingConvention { CallingConventionAPCS, CallingConventionAAPCS, CallingConventionAAPCSVFP, + CallingConventionSysV }; // This one corresponds to the builtin.zig enum. diff --git a/zig/src/stage1/analyze.cpp b/zig/src/stage1/analyze.cpp index 21ad55b8f7..8ca845d2cf 100644 --- a/zig/src/stage1/analyze.cpp +++ b/zig/src/stage1/analyze.cpp @@ -974,6 +974,7 @@ const char *calling_convention_name(CallingConvention cc) { case CallingConventionAAPCS: return "AAPCS"; case CallingConventionAAPCSVFP: return "AAPCSVFP"; case CallingConventionInline: return "Inline"; + case CallingConventionSysV: return "SysV"; } zig_unreachable(); } @@ -995,6 +996,7 @@ bool calling_convention_allows_zig_types(CallingConvention cc) { case CallingConventionAPCS: case CallingConventionAAPCS: case CallingConventionAAPCSVFP: + case CallingConventionSysV: return false; } zig_unreachable(); @@ -1969,6 +1971,10 @@ Error emit_error_unless_callconv_allowed_for_target(CodeGen *g, AstNode *source_ case CallingConventionAAPCSVFP: if (!target_is_arm(g->zig_target)) allowed_platforms = "ARM"; + break; + case CallingConventionSysV: + if (g->zig_target->arch != ZigLLVM_x86_64) + allowed_platforms = "x86_64"; } if (allowed_platforms != nullptr) { add_node_error(g, source_node, buf_sprintf( @@ -3805,6 +3811,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { case CallingConventionAPCS: case CallingConventionAAPCS: case CallingConventionAAPCSVFP: + case CallingConventionSysV: add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name), GlobalLinkageIdStrong, fn_cc); break; @@ -4769,11 +4776,11 @@ Error type_is_nonnull_ptr2(CodeGen *g, ZigType *type, bool *result) { return ErrorNone; } -static uint32_t get_async_frame_align_bytes(CodeGen *g) { - uint32_t a = g->pointer_size_bytes * 2; - // promises have at least alignment 8 so that we can have 3 extra bits when doing atomicrmw - if (a < 8) a = 8; - return a; +uint32_t get_async_frame_align_bytes(CodeGen *g) { + // Due to how the frame structure is built the minimum alignment is the one + // of a usize (or pointer). + // label (grep this): [fn_frame_struct_layout] + return max(g->builtin_types.entry_usize->abi_align, target_fn_align(g->zig_target)); } uint32_t get_ptr_align(CodeGen *g, ZigType *type) { @@ -4789,11 +4796,8 @@ uint32_t get_ptr_align(CodeGen *g, ZigType *type) { return (ptr_type->data.pointer.explicit_alignment == 0) ? get_abi_alignment(g, ptr_type->data.pointer.child_type) : ptr_type->data.pointer.explicit_alignment; } else if (ptr_type->id == ZigTypeIdFn) { - // I tried making this use LLVMABIAlignmentOfType but it trips this assertion in LLVM: - // "Cannot getTypeInfo() on a type that is unsized!" - // when getting the alignment of `?fn() callconv(.C) void`. - // See http://lists.llvm.org/pipermail/llvm-dev/2018-September/126142.html - return (ptr_type->data.fn.fn_type_id.alignment == 0) ? 1 : ptr_type->data.fn.fn_type_id.alignment; + return (ptr_type->data.fn.fn_type_id.alignment == 0) ? + target_fn_ptr_align(g->zig_target) : ptr_type->data.fn.fn_type_id.alignment; } else if (ptr_type->id == ZigTypeIdAnyFrame) { return get_async_frame_align_bytes(g); } else { diff --git a/zig/src/stage1/analyze.hpp b/zig/src/stage1/analyze.hpp index cea48b893c..2815274f63 100644 --- a/zig/src/stage1/analyze.hpp +++ b/zig/src/stage1/analyze.hpp @@ -47,6 +47,7 @@ ZigType *get_test_fn_type(CodeGen *g); ZigType *get_any_frame_type(CodeGen *g, ZigType *result_type); bool handle_is_ptr(CodeGen *g, ZigType *type_entry); Error emit_error_unless_callconv_allowed_for_target(CodeGen *g, AstNode *source_node, CallingConvention cc); +uint32_t get_async_frame_align_bytes(CodeGen *g); bool type_has_bits(CodeGen *g, ZigType *type_entry); Error type_has_bits2(CodeGen *g, ZigType *type_entry, bool *result); diff --git a/zig/src/stage1/bigfloat.cpp b/zig/src/stage1/bigfloat.cpp index 58b0aff54a..840cdccc8b 100644 --- a/zig/src/stage1/bigfloat.cpp +++ b/zig/src/stage1/bigfloat.cpp @@ -9,6 +9,7 @@ #include "bigint.hpp" #include "buffer.hpp" #include "softfloat.hpp" +#include "softfloat_ext.hpp" #include "parse_f128.h" #include #include @@ -60,9 +61,7 @@ void bigfloat_init_bigint(BigFloat *dest, const BigInt *op) { if (i == 0) { if (op->is_negative) { - float128_t zero_f128; - ui32_to_f128M(0, &zero_f128); - f128M_sub(&zero_f128, &dest->value, &dest->value); + f128M_neg(&dest->value, &dest->value); } return; } @@ -89,9 +88,7 @@ void bigfloat_add(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) { } void bigfloat_negate(BigFloat *dest, const BigFloat *op) { - float128_t zero_f128; - ui32_to_f128M(0, &zero_f128); - f128M_sub(&zero_f128, &op->value, &dest->value); + f128M_neg(&op->value, &dest->value); } void bigfloat_sub(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) { diff --git a/zig/src/stage1/bigint.cpp b/zig/src/stage1/bigint.cpp index 79a05e95a5..acb3e18e41 100644 --- a/zig/src/stage1/bigint.cpp +++ b/zig/src/stage1/bigint.cpp @@ -1446,10 +1446,10 @@ void bigint_negate(BigInt *dest, const BigInt *op) { bigint_normalize(dest); } -void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count) { +void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) { BigInt zero; bigint_init_unsigned(&zero, 0); - bigint_sub_wrap(dest, &zero, op, bit_count, true); + bigint_sub_wrap(dest, &zero, op, bit_count, is_signed); } void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) { diff --git a/zig/src/stage1/bigint.hpp b/zig/src/stage1/bigint.hpp index 044ea66423..aa37b9302a 100644 --- a/zig/src/stage1/bigint.hpp +++ b/zig/src/stage1/bigint.hpp @@ -75,7 +75,7 @@ void bigint_shl_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2); void bigint_negate(BigInt *dest, const BigInt *op); -void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count); +void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed); void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed); void bigint_truncate(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed); diff --git a/zig/src/stage1/codegen.cpp b/zig/src/stage1/codegen.cpp index 6d219c517d..ede15e4394 100644 --- a/zig/src/stage1/codegen.cpp +++ b/zig/src/stage1/codegen.cpp @@ -204,6 +204,9 @@ static ZigLLVM_CallingConv get_llvm_cc(CodeGen *g, CallingConvention cc) { case CallingConventionSignal: assert(g->zig_target->arch == ZigLLVM_avr); return ZigLLVM_AVR_SIGNAL; + case CallingConventionSysV: + assert(g->zig_target->arch == ZigLLVM_x86_64); + return ZigLLVM_X86_64_SysV; } zig_unreachable(); } @@ -348,6 +351,7 @@ static bool cc_want_sret_attr(CallingConvention cc) { case CallingConventionAPCS: case CallingConventionAAPCS: case CallingConventionAAPCSVFP: + case CallingConventionSysV: return true; case CallingConventionAsync: case CallingConventionUnspecified: @@ -7436,7 +7440,10 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n case ZigTypeIdFloat: switch (type_entry->data.floating.bit_count) { case 16: - return LLVMConstReal(get_llvm_type(g, type_entry), zig_f16_to_double(const_val->data.x_f16)); + { + LLVMValueRef as_int = LLVMConstInt(LLVMInt16Type(), const_val->data.x_f16.v, false); + return LLVMConstBitCast(as_int, get_llvm_type(g, type_entry)); + } case 32: return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f32); case 64: @@ -9074,6 +9081,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { static_assert(CallingConventionAPCS == 11, ""); static_assert(CallingConventionAAPCS == 12, ""); static_assert(CallingConventionAAPCSVFP == 13, ""); + static_assert(CallingConventionSysV == 14, ""); static_assert(BuiltinPtrSizeOne == 0, ""); static_assert(BuiltinPtrSizeMany == 1, ""); diff --git a/zig/src/stage1/ir.cpp b/zig/src/stage1/ir.cpp index 71a233c964..2f345a8411 100644 --- a/zig/src/stage1/ir.cpp +++ b/zig/src/stage1/ir.cpp @@ -9534,7 +9534,7 @@ static IrInstSrc *ir_gen_nosuspend(IrBuilderSrc *irb, Scope *parent_scope, AstNo Scope *child_scope = create_nosuspend_scope(irb->codegen, node, parent_scope); // purposefully pass null for result_loc and let EndExpr handle it - return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval, nullptr); + return ir_gen_node_extra(irb, node->data.nosuspend_expr.expr, child_scope, lval, nullptr); } static IrInstSrc *ir_gen_return_from_block(IrBuilderSrc *irb, Scope *break_scope, AstNode *node, ScopeBlock *block_scope) { @@ -10199,14 +10199,12 @@ static IrInstSrc *ir_gen_suspend(IrBuilderSrc *irb, Scope *parent_scope, AstNode } IrInstSrcSuspendBegin *begin = ir_build_suspend_begin_src(irb, parent_scope, node); - if (node->data.suspend.block != nullptr) { - ScopeSuspend *suspend_scope = create_suspend_scope(irb->codegen, node, parent_scope); - Scope *child_scope = &suspend_scope->base; - IrInstSrc *susp_res = ir_gen_node(irb, node->data.suspend.block, child_scope); - if (susp_res == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.suspend.block, susp_res)); - } + ScopeSuspend *suspend_scope = create_suspend_scope(irb->codegen, node, parent_scope); + Scope *child_scope = &suspend_scope->base; + IrInstSrc *susp_res = ir_gen_node(irb, node->data.suspend.block, child_scope); + if (susp_res == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.suspend.block, susp_res)); return ir_mark_gen(ir_build_suspend_finish_src(irb, parent_scope, node, begin)); } @@ -11363,11 +11361,8 @@ static void float_negate(ZigValue *out_val, ZigValue *op) { } else if (op->type->id == ZigTypeIdFloat) { switch (op->type->data.floating.bit_count) { case 16: - { - const float16_t zero = zig_double_to_f16(0); - out_val->data.x_f16 = f16_sub(zero, op->data.x_f16); - return; - } + out_val->data.x_f16 = f16_neg(op->data.x_f16); + return; case 32: out_val->data.x_f32 = -op->data.x_f32; return; @@ -11375,9 +11370,7 @@ static void float_negate(ZigValue *out_val, ZigValue *op) { out_val->data.x_f64 = -op->data.x_f64; return; case 128: - float128_t zero_f128; - ui32_to_f128M(0, &zero_f128); - f128M_sub(&zero_f128, &op->data.x_f128, &out_val->data.x_f128); + f128M_neg(&op->data.x_f128, &out_val->data.x_f128); return; default: zig_unreachable(); @@ -19223,6 +19216,7 @@ static IrInstGen *ir_analyze_instruction_export(IrAnalyze *ira, IrInstSrcExport case CallingConventionAPCS: case CallingConventionAAPCS: case CallingConventionAAPCSVFP: + case CallingConventionSysV: add_fn_export(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, cc); fn_entry->section_name = section_name; break; @@ -20666,8 +20660,12 @@ static IrInstGen *analyze_casted_new_stack(IrAnalyze *ira, IrInst* source_instr, get_fn_frame_type(ira->codegen, fn_entry), false); return ir_implicit_cast(ira, new_stack, needed_frame_type); } else { + // XXX The stack alignment is hardcoded to 16 here and in + // std.Target.stack_align. + const uint32_t required_align = is_async_call_builtin ? + get_async_frame_align_bytes(ira->codegen) : 16; ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, - false, false, PtrLenUnknown, target_fn_align(ira->codegen->zig_target), 0, 0, false); + false, false, PtrLenUnknown, required_align, 0, 0, false); ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr); ira->codegen->need_frame_size_prefix_data = true; return ir_implicit_cast2(ira, new_stack_src, new_stack, u8_slice); @@ -21665,8 +21663,8 @@ static ErrorMsg *ir_eval_negation_scalar(IrAnalyze *ira, IrInst* source_instr, Z { bool is_float = (scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat); - bool ok_type = ((scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) || - scalar_type->id == ZigTypeIdComptimeInt || (is_float && !is_wrap_op)); + bool ok_type = scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdComptimeInt || + (is_float && !is_wrap_op); if (!ok_type) { const char *fmt = is_wrap_op ? "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'"; @@ -21677,7 +21675,7 @@ static ErrorMsg *ir_eval_negation_scalar(IrAnalyze *ira, IrInst* source_instr, Z float_negate(scalar_out_val, operand_val); } else if (is_wrap_op) { bigint_negate_wrap(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint, - scalar_type->data.integral.bit_count); + scalar_type->data.integral.bit_count, scalar_type->data.integral.is_signed); } else { bigint_negate(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint); } @@ -26086,11 +26084,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy fields[0]->special = ConstValSpecialStatic; fields[0]->type = get_builtin_type(ira->codegen, "CallingConvention"); bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.fn.fn_type_id.cc); - // alignment: u29 + // alignment: comptime_int ensure_field_index(result->type, "alignment", 1); fields[1]->special = ConstValSpecialStatic; fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int; - bigint_init_unsigned(&fields[1]->data.x_bigint, type_entry->data.fn.fn_type_id.alignment); + bigint_init_unsigned(&fields[1]->data.x_bigint, get_ptr_align(ira->codegen, type_entry)); // is_generic: bool ensure_field_index(result->type, "is_generic", 2); bool is_generic = type_entry->data.fn.is_generic; @@ -30102,7 +30100,7 @@ static IrInstGen *ir_align_cast(IrAnalyze *ira, IrInstGen *target, uint32_t alig fn_type_id.alignment = align_bytes; result_type = get_fn_type(ira->codegen, &fn_type_id); } else if (target_type->id == ZigTypeIdAnyFrame) { - if (align_bytes >= target_fn_align(ira->codegen->zig_target)) { + if (align_bytes >= get_async_frame_align_bytes(ira->codegen)) { result_type = target_type; } else { ir_add_error(ira, &target->base, buf_sprintf("sub-aligned anyframe not allowed")); diff --git a/zig/src/stage1/parser.cpp b/zig/src/stage1/parser.cpp index c37b3ffefb..d57277cd51 100644 --- a/zig/src/stage1/parser.cpp +++ b/zig/src/stage1/parser.cpp @@ -946,10 +946,7 @@ static AstNode *ast_parse_statement(ParseContext *pc) { Token *suspend = eat_token_if(pc, TokenIdKeywordSuspend); if (suspend != nullptr) { - AstNode *statement = nullptr; - if (eat_token_if(pc, TokenIdSemicolon) == nullptr) - statement = ast_expect(pc, ast_parse_block_expr_statement); - + AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement); AstNode *res = ast_create_node(pc, NodeTypeSuspend, suspend); res->data.suspend.block = statement; return res; diff --git a/zig/src/stage1/softfloat_ext.cpp b/zig/src/stage1/softfloat_ext.cpp index 8408a15116..d0b8d1a5b3 100644 --- a/zig/src/stage1/softfloat_ext.cpp +++ b/zig/src/stage1/softfloat_ext.cpp @@ -1,17 +1,21 @@ #include "softfloat_ext.hpp" +#include "zigendian.h" extern "C" { #include "softfloat.h" } void f128M_abs(const float128_t *aPtr, float128_t *zPtr) { - float128_t zero_float; - ui32_to_f128M(0, &zero_float); - if (f128M_lt(aPtr, &zero_float)) { - f128M_sub(&zero_float, aPtr, zPtr); - } else { - *zPtr = *aPtr; - } + // Clear the sign bit. +#if ZIG_BYTE_ORDER == ZIG_LITTLE_ENDIAN + zPtr->v[1] = aPtr->v[1] & ~(UINT64_C(1) << 63); + zPtr->v[0] = aPtr->v[0]; +#elif ZIG_BYTE_ORDER == ZIG_BIG_ENDIAN + zPtr->v[0] = aPtr->v[0] & ~(UINT64_C(1) << 63); + zPtr->v[1] = aPtr->v[1]; +#else +#error Unsupported endian +#endif } void f128M_trunc(const float128_t *aPtr, float128_t *zPtr) { @@ -22,4 +26,24 @@ void f128M_trunc(const float128_t *aPtr, float128_t *zPtr) { } else { f128M_roundToInt(aPtr, softfloat_round_min, false, zPtr); } +} + +float16_t f16_neg(const float16_t a) { + union { uint16_t ui; float16_t f; } uA; + // Toggle the sign bit. + uA.ui = a.v ^ (UINT16_C(1) << 15); + return uA.f; +} + +void f128M_neg(const float128_t *aPtr, float128_t *zPtr) { + // Toggle the sign bit. +#if ZIG_BYTE_ORDER == ZIG_LITTLE_ENDIAN + zPtr->v[1] = aPtr->v[1] ^ (UINT64_C(1) << 63); + zPtr->v[0] = aPtr->v[0]; +#elif ZIG_BYTE_ORDER == ZIG_BIG_ENDIAN + zPtr->v[0] = aPtr->v[0] ^ (UINT64_C(1) << 63); + zPtr->v[1] = aPtr->v[1]; +#else +#error Unsupported endian +#endif } \ No newline at end of file diff --git a/zig/src/stage1/softfloat_ext.hpp b/zig/src/stage1/softfloat_ext.hpp index 0a1f958933..42922a5226 100644 --- a/zig/src/stage1/softfloat_ext.hpp +++ b/zig/src/stage1/softfloat_ext.hpp @@ -5,5 +5,8 @@ void f128M_abs(const float128_t *aPtr, float128_t *zPtr); void f128M_trunc(const float128_t *aPtr, float128_t *zPtr); +void f128M_neg(const float128_t *aPtr, float128_t *zPtr); + +float16_t f16_neg(const float16_t a); #endif \ No newline at end of file diff --git a/zig/src/stage1/stage1.h b/zig/src/stage1/stage1.h index 59632b9877..6413914f6e 100644 --- a/zig/src/stage1/stage1.h +++ b/zig/src/stage1/stage1.h @@ -56,7 +56,7 @@ enum TargetSubsystem { // ABI warning -// Synchronize with target.cpp::os_list +// Synchronize with std.Target.Os.Tag and target.cpp::os_list enum Os { OsFreestanding, OsAnanas, @@ -94,6 +94,9 @@ enum Os { OsWASI, OsEmscripten, OsUefi, + OsOpenCL, + OsGLSL450, + OsVulkan, OsOther, }; diff --git a/zig/src/stage1/target.cpp b/zig/src/stage1/target.cpp index 028f6e5fa8..5a1e18e152 100644 --- a/zig/src/stage1/target.cpp +++ b/zig/src/stage1/target.cpp @@ -122,6 +122,9 @@ static const Os os_list[] = { OsWASI, OsEmscripten, OsUefi, + OsOpenCL, + OsGLSL450, + OsVulkan, OsOther, }; @@ -213,6 +216,9 @@ Os target_os_enum(size_t index) { ZigLLVM_OSType get_llvm_os_type(Os os_type) { switch (os_type) { case OsFreestanding: + case OsOpenCL: + case OsGLSL450: + case OsVulkan: case OsOther: return ZigLLVM_UnknownOS; case OsAnanas: @@ -330,6 +336,9 @@ const char *target_os_name(Os os_type) { case OsHurd: case OsWASI: case OsEmscripten: + case OsOpenCL: + case OsGLSL450: + case OsVulkan: return ZigLLVMGetOSTypeName(get_llvm_os_type(os_type)); } zig_unreachable(); @@ -733,6 +742,9 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { case OsAMDPAL: case OsHermitCore: case OsHurd: + case OsOpenCL: + case OsGLSL450: + case OsVulkan: zig_panic("TODO c type size in bits for this target"); } zig_unreachable(); @@ -999,6 +1011,10 @@ ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) { case OsWASI: case OsEmscripten: return ZigLLVM_Musl; + case OsOpenCL: + case OsGLSL450: + case OsVulkan: + return ZigLLVM_UnknownEnvironment; } zig_unreachable(); } @@ -1253,6 +1269,37 @@ bool target_is_ppc(const ZigTarget *target) { target->arch == ZigLLVM_ppc64le; } +// Returns the minimum alignment for every function pointer on the given +// architecture. +unsigned target_fn_ptr_align(const ZigTarget *target) { + // TODO This is a pessimization but is always correct. + return 1; +} + +// Returns the minimum alignment for every function on the given architecture. unsigned target_fn_align(const ZigTarget *target) { - return 16; + switch (target->arch) { + case ZigLLVM_riscv32: + case ZigLLVM_riscv64: + // TODO If the C extension is not present the value is 4. + return 2; + case ZigLLVM_ppc: + case ZigLLVM_ppcle: + case ZigLLVM_ppc64: + case ZigLLVM_ppc64le: + case ZigLLVM_aarch64: + case ZigLLVM_aarch64_be: + case ZigLLVM_aarch64_32: + case ZigLLVM_sparc: + case ZigLLVM_sparcel: + case ZigLLVM_sparcv9: + case ZigLLVM_mips: + case ZigLLVM_mipsel: + case ZigLLVM_mips64: + case ZigLLVM_mips64el: + return 4; + + default: + return 1; + } } diff --git a/zig/src/stage1/target.hpp b/zig/src/stage1/target.hpp index 099c3c1e78..362d63eb32 100644 --- a/zig/src/stage1/target.hpp +++ b/zig/src/stage1/target.hpp @@ -98,6 +98,7 @@ size_t target_libc_count(void); void target_libc_enum(size_t index, ZigTarget *out_target); bool target_libc_needs_crti_crtn(const ZigTarget *target); +unsigned target_fn_ptr_align(const ZigTarget *target); unsigned target_fn_align(const ZigTarget *target); #endif diff --git a/zig/src/translate_c.zig b/zig/src/translate_c.zig index 9a1215abd6..8244d66e94 100644 --- a/zig/src/translate_c.zig +++ b/zig/src/translate_c.zig @@ -8,6 +8,7 @@ const ctok = std.c.tokenizer; const CToken = std.c.Token; const mem = std.mem; const math = std.math; +const meta = std.meta; const ast = @import("translate_c/ast.zig"); const Node = ast.Node; const Tag = Node.Tag; @@ -1353,10 +1354,14 @@ fn transCreatePointerArithmeticSignedOp( const bitcast_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node); - const arith_args = .{ .lhs = lhs_node, .rhs = bitcast_node }; - const arith_node = try if (is_add) Tag.add.create(c.arena, arith_args) else Tag.sub.create(c.arena, arith_args); - - return maybeSuppressResult(c, scope, result_used, arith_node); + return transCreateNodeInfixOp( + c, + scope, + if (is_add) .add else .sub, + lhs_node, + bitcast_node, + result_used, + ); } fn transBinaryOperator( @@ -1737,7 +1742,7 @@ fn transImplicitCastExpr( } fn isBuiltinDefined(name: []const u8) bool { - inline for (std.meta.declarations(c_builtins)) |decl| { + inline for (meta.declarations(c_builtins)) |decl| { if (std.mem.eql(u8, name, decl.name)) return true; } return false; @@ -2161,8 +2166,8 @@ fn transCCast( return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = bool_to_int }); } if (cIsEnum(dst_type)) { - // @intToEnum(dest_type, val) - return Tag.int_to_enum.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); + // import("std").meta.cast(dest_type, val) + return Tag.std_meta_cast.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); } if (cIsEnum(src_type) and !cIsEnum(dst_type)) { // @enumToInt(val) @@ -3153,7 +3158,7 @@ const ClangFunctionType = union(enum) { NoProto: *const clang.FunctionType, fn getReturnType(self: @This()) clang.QualType { - switch (@as(std.meta.Tag(@This()), self)) { + switch (@as(meta.Tag(@This()), self)) { .Proto => return self.Proto.getReturnType(), .NoProto => return self.NoProto.getReturnType(), } @@ -3535,7 +3540,7 @@ fn transCPtrCast( expr else blk: { const child_type_node = try transQualType(c, scope, child_type, loc); - const alignof = try Tag.alignof.create(c.arena, child_type_node); + const alignof = try Tag.std_meta_alignment.create(c.arena, child_type_node); const align_cast = try Tag.align_cast.create(c.arena, .{ .lhs = alignof, .rhs = expr }); break :blk align_cast; }; @@ -3543,9 +3548,22 @@ fn transCPtrCast( } } -fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node { +fn transFloatingLiteral(c: *Context, scope: *Scope, expr: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node { + switch (expr.getRawSemantics()) { + .IEEEhalf, // f16 + .IEEEsingle, // f32 + .IEEEdouble, // f64 + => {}, + else => |format| return fail( + c, + error.UnsupportedTranslation, + expr.getBeginLoc(), + "unsupported floating point constant format {}", + .{format}, + ), + } // TODO use something more accurate - var dbl = stmt.getValueAsApproximateDouble(); + var dbl = expr.getValueAsApproximateDouble(); const is_negative = dbl < 0; if (is_negative) dbl = -dbl; const str = if (dbl == std.math.floor(dbl)) @@ -4089,7 +4107,7 @@ fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !Node { } fn transCreateNodeNumber(c: *Context, num: anytype, num_kind: enum { int, float }) !Node { - const fmt_s = if (comptime std.meta.trait.isNumber(@TypeOf(num))) "{d}" else "{s}"; + const fmt_s = if (comptime meta.trait.isNumber(@TypeOf(num))) "{d}" else "{s}"; const str = try std.fmt.allocPrint(c.arena, fmt_s, .{num}); if (num_kind == .float) return Tag.float_literal.create(c.arena, str) @@ -4398,6 +4416,7 @@ fn transCC( .X86ThisCall => return CallingConvention.Thiscall, .AAPCS => return CallingConvention.AAPCS, .AAPCS_VFP => return CallingConvention.AAPCSVFP, + .X86_64SysV => return CallingConvention.SysV, else => return fail( c, error.UnsupportedType, @@ -4844,12 +4863,12 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node { // make the output less noisy by skipping promoteIntLiteral where // it's guaranteed to not be required because of C standard type constraints const guaranteed_to_fit = switch (suffix) { - .none => if (math.cast(i16, value)) |_| true else |_| false, - .u => if (math.cast(u16, value)) |_| true else |_| false, - .l => if (math.cast(i32, value)) |_| true else |_| false, - .lu => if (math.cast(u32, value)) |_| true else |_| false, - .ll => if (math.cast(i64, value)) |_| true else |_| false, - .llu => if (math.cast(u64, value)) |_| true else |_| false, + .none => !meta.isError(math.cast(i16, value)), + .u => !meta.isError(math.cast(u16, value)), + .l => !meta.isError(math.cast(i32, value)), + .lu => !meta.isError(math.cast(u32, value)), + .ll => !meta.isError(math.cast(i64, value)), + .llu => !meta.isError(math.cast(u64, value)), .f => unreachable, }; diff --git a/zig/src/translate_c/ast.zig b/zig/src/translate_c/ast.zig index 4be0fead97..2e75bf1182 100644 --- a/zig/src/translate_c/ast.zig +++ b/zig/src/translate_c/ast.zig @@ -120,8 +120,11 @@ pub const Node = extern union { std_math_Log2Int, /// @intCast(lhs, rhs) int_cast, - /// @rem(lhs, rhs) + /// @import("std").meta.promoteIntLiteral(value, type, radix) std_meta_promoteIntLiteral, + /// @import("std").meta.alignment(value) + std_meta_alignment, + /// @rem(lhs, rhs) rem, /// @divTrunc(lhs, rhs) div_trunc, @@ -260,6 +263,7 @@ pub const Node = extern union { .switch_else, .block_single, .std_meta_sizeof, + .std_meta_alignment, .bool_to_int, .sizeof, .alignof, @@ -876,6 +880,11 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { const import_node = try renderStdImport(c, "meta", "promoteIntLiteral"); return renderCall(c, import_node, &.{ payload.type, payload.value, payload.radix }); }, + .std_meta_alignment => { + const payload = node.castTag(.std_meta_alignment).?.data; + const import_node = try renderStdImport(c, "meta", "alignment"); + return renderCall(c, import_node, &.{payload}); + }, .std_meta_sizeof => { const payload = node.castTag(.std_meta_sizeof).?.data; const import_node = try renderStdImport(c, "meta", "sizeof"); @@ -1665,7 +1674,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }, .array_access => { const payload = node.castTag(.array_access).?.data; - const lhs = try renderNode(c, payload.lhs); + const lhs = try renderNodeGrouped(c, payload.lhs); const l_bracket = try c.addToken(.l_bracket, "["); const index_expr = try renderNode(c, payload.rhs); _ = try c.addToken(.r_bracket, "]"); @@ -1728,7 +1737,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }, .field_access => { const payload = node.castTag(.field_access).?.data; - const lhs = try renderNode(c, payload.lhs); + const lhs = try renderNodeGrouped(c, payload.lhs); return renderFieldAccess(c, lhs, payload.field_name); }, .@"struct", .@"union" => return renderRecord(c, node), @@ -2073,7 +2082,7 @@ fn renderNullSentinelArrayType(c: *Context, len: usize, elem_type: Node) !NodeIn .main_token = l_bracket, .data = .{ .lhs = len_expr, - .rhs = try c.addExtra(std.zig.ast.Node.ArrayTypeSentinel { + .rhs = try c.addExtra(std.zig.ast.Node.ArrayTypeSentinel{ .sentinel = sentinel_expr, .elem_type = elem_type_expr, }), @@ -2144,6 +2153,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { .typeof, .typeinfo, .std_meta_sizeof, + .std_meta_alignment, .std_meta_cast, .std_meta_promoteIntLiteral, .std_meta_vector, diff --git a/zig/src/zig_clang.cpp b/zig/src/zig_clang.cpp index 32bb9b4487..efdef1e26b 100644 --- a/zig/src/zig_clang.cpp +++ b/zig/src/zig_clang.cpp @@ -2528,6 +2528,11 @@ double ZigClangFloatingLiteral_getValueAsApproximateDouble(const ZigClangFloatin return casted->getValueAsApproximateDouble(); } +ZigClangAPFloatBase_Semantics ZigClangFloatingLiteral_getRawSemantics(const ZigClangFloatingLiteral *self) { + auto casted = reinterpret_cast(self); + return static_cast(casted->getRawSemantics()); +} + enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self) { auto casted = reinterpret_cast(self); return (ZigClangStringLiteral_StringKind)casted->getKind(); diff --git a/zig/src/zig_clang.h b/zig/src/zig_clang.h index eac3592692..4ea8cc731b 100644 --- a/zig/src/zig_clang.h +++ b/zig/src/zig_clang.h @@ -881,6 +881,16 @@ enum ZigClangAPFloat_roundingMode { ZigClangAPFloat_roundingMode_Invalid = -1, }; +enum ZigClangAPFloatBase_Semantics { + ZigClangAPFloatBase_Semantics_IEEEhalf, + ZigClangAPFloatBase_Semantics_BFloat, + ZigClangAPFloatBase_Semantics_IEEEsingle, + ZigClangAPFloatBase_Semantics_IEEEdouble, + ZigClangAPFloatBase_Semantics_x87DoubleExtended, + ZigClangAPFloatBase_Semantics_IEEEquad, + ZigClangAPFloatBase_Semantics_PPCDoubleDouble, +}; + enum ZigClangStringLiteral_StringKind { ZigClangStringLiteral_StringKind_Ascii, ZigClangStringLiteral_StringKind_Wide, @@ -1142,6 +1152,7 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangDeclStmt_getBeginLoc(const st ZIG_EXTERN_C unsigned ZigClangAPFloat_convertToHexString(const struct ZigClangAPFloat *self, char *DST, unsigned HexDigits, bool UpperCase, enum ZigClangAPFloat_roundingMode RM); ZIG_EXTERN_C double ZigClangFloatingLiteral_getValueAsApproximateDouble(const ZigClangFloatingLiteral *self); +ZIG_EXTERN_C ZigClangAPFloatBase_Semantics ZigClangFloatingLiteral_getRawSemantics(const ZigClangFloatingLiteral *self); ZIG_EXTERN_C enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self); ZIG_EXTERN_C uint32_t ZigClangStringLiteral_getCodeUnit(const struct ZigClangStringLiteral *self, size_t i); diff --git a/zig/test/compile_errors.zig b/zig/test/compile_errors.zig index 808f0b31dc..5b36027248 100644 --- a/zig/test/compile_errors.zig +++ b/zig/test/compile_errors.zig @@ -1021,7 +1021,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ nosuspend { \\ const bar = async foo(); - \\ suspend; + \\ suspend {} \\ resume bar; \\ } \\} @@ -2120,7 +2120,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ non_async_fn = func; \\} \\fn func() void { - \\ suspend; + \\ suspend {} \\} , &[_][]const u8{ "tmp.zig:5:1: error: 'func' cannot be async", @@ -2136,7 +2136,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\fn func() callconv(.Async) void {} , &[_][]const u8{ - "tmp.zig:4:21: error: expected type '[]align(16) u8', found '*[64]u8'", + // Split the check in two as the alignment value is target dependent. + "tmp.zig:4:21: error: expected type '[]align(", + ") u8', found '*[64]u8'", }); cases.add("atomic orderings of fence Acquire or stricter", @@ -2198,7 +2200,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x: anyframe = &f; \\} \\fn func() void { - \\ suspend; + \\ suspend {} \\} , &[_][]const u8{ "tmp.zig:3:12: error: expected type 'anyframe', found '*const @Frame(func)'", @@ -2231,10 +2233,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ frame = async bar(); \\} \\fn foo() void { - \\ suspend; + \\ suspend {} \\} \\fn bar() void { - \\ suspend; + \\ suspend {} \\} , &[_][]const u8{ "tmp.zig:3:13: error: expected type '*@Frame(bar)', found '*@Frame(foo)'", @@ -2269,7 +2271,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var result = await frame; \\} \\fn func() void { - \\ suspend; + \\ suspend {} \\} , &[_][]const u8{ "tmp.zig:1:1: error: function with calling convention 'C' cannot be async", @@ -2347,7 +2349,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ bar(); \\} \\fn bar() void { - \\ suspend; + \\ suspend {} \\} , &[_][]const u8{ "tmp.zig:1:1: error: function with calling convention 'C' cannot be async", diff --git a/zig/test/run_translated_c.zig b/zig/test/run_translated_c.zig index 7e5d0da367..314ef2889d 100644 --- a/zig/test/run_translated_c.zig +++ b/zig/test/run_translated_c.zig @@ -1453,4 +1453,27 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ return 0; \\} , ""); + + cases.add("Cast to enum from larger integral type. Issue #6011", + \\#include + \\#include + \\enum Foo { A, B, C }; + \\static inline enum Foo do_stuff(void) { + \\ int64_t i = 1; + \\ return (enum Foo)i; + \\} + \\int main(void) { + \\ if (do_stuff() != B) abort(); + \\ return 0; + \\} + , ""); + + cases.add("Render array LHS as grouped node if necessary", + \\#include + \\int main(void) { + \\ int arr[] = {40, 41, 42, 43}; + \\ if ((arr + 1)[1] != 42) abort(); + \\ return 0; + \\} + , ""); } diff --git a/zig/test/runtime_safety.zig b/zig/test/runtime_safety.zig index eb49b2dbc1..03a26a2c32 100644 --- a/zig/test/runtime_safety.zig +++ b/zig/test/runtime_safety.zig @@ -13,7 +13,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addRuntimeSafety("switch on corrupted enum value", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\const E = enum(u32) { \\ X = 1, \\}; @@ -28,7 +28,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addRuntimeSafety("switch on corrupted union value", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\const U = union(enum(u32)) { \\ X: u8, \\}; @@ -54,7 +54,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addRuntimeSafety("@tagName on corrupted enum value", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\const E = enum(u32) { \\ X = 1, \\}; @@ -67,7 +67,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addRuntimeSafety("@tagName on corrupted union value", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\const U = union(enum(u32)) { \\ X: u8, \\}; @@ -92,7 +92,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addRuntimeSafety("slicing operator with sentinel", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\pub fn main() void { \\ var buf = [4]u8{'a','b','c',0}; \\ const slice = buf[0..4 :0]; @@ -100,7 +100,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("slicing operator with sentinel", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\pub fn main() void { \\ var buf = [4]u8{'a','b','c',0}; \\ const slice = buf[0..:0]; @@ -108,7 +108,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("slicing operator with sentinel", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\pub fn main() void { \\ var buf_zero = [0]u8{}; \\ const slice = buf_zero[0..0 :0]; @@ -116,7 +116,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("slicing operator with sentinel", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\pub fn main() void { \\ var buf_zero = [0]u8{}; \\ const slice = buf_zero[0..:0]; @@ -124,7 +124,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("slicing operator with sentinel", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\pub fn main() void { \\ var buf_sentinel = [2:0]u8{'a','b'}; \\ @ptrCast(*[3]u8, &buf_sentinel)[2] = 0; @@ -133,7 +133,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("slicing operator with sentinel", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\pub fn main() void { \\ var buf_slice: []const u8 = &[3]u8{ 'a', 'b', 0 }; \\ const slice = buf_slice[0..3 :0]; @@ -141,7 +141,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("slicing operator with sentinel", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\pub fn main() void { \\ var buf_slice: []const u8 = &[3]u8{ 'a', 'b', 0 }; \\ const slice = buf_slice[0.. :0]; @@ -367,7 +367,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\} \\fn add(a: i32, b: i32) i32 { \\ if (a > 100) { - \\ suspend; + \\ suspend {} \\ } \\ return a + b; \\} @@ -407,7 +407,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ var frame = @asyncCall(&bytes, {}, ptr, .{}); \\} \\fn other() callconv(.Async) void { - \\ suspend; + \\ suspend {} \\} ); @@ -424,7 +424,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ await frame; \\} \\fn other() void { - \\ suspend; + \\ suspend {} \\} ); @@ -440,7 +440,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ other(); \\} \\fn other() void { - \\ suspend; + \\ suspend {} \\} ); @@ -454,7 +454,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ resume p; //bad \\} \\fn suspendOnce() void { - \\ suspend; + \\ suspend {} \\} ); @@ -1019,7 +1019,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\} \\ \\fn failing() anyerror!void { - \\ suspend; + \\ suspend {} \\ return second(); \\} \\ diff --git a/zig/test/stage1/behavior/async_fn.zig b/zig/test/stage1/behavior/async_fn.zig index 40269df5ec..0765eac7e8 100644 --- a/zig/test/stage1/behavior/async_fn.zig +++ b/zig/test/stage1/behavior/async_fn.zig @@ -18,9 +18,9 @@ test "simple coroutine suspend and resume" { } fn simpleAsyncFn() void { global_x += 1; - suspend; + suspend {} global_x += 1; - suspend; + suspend {} global_x += 1; } @@ -34,7 +34,7 @@ test "pass parameter to coroutine" { } fn simpleAsyncFnWithArg(delta: i32) void { global_y += delta; - suspend; + suspend {} global_y += delta; } @@ -50,7 +50,7 @@ test "suspend at end of function" { fn suspendAtEnd() void { x += 1; - suspend; + suspend {} } }; S.doTheTest(); @@ -74,11 +74,11 @@ test "local variable in async function" { fn add(a: i32, b: i32) void { var accum: i32 = 0; - suspend; + suspend {} accum += a; - suspend; + suspend {} accum += b; - suspend; + suspend {} x = accum; } }; @@ -102,7 +102,7 @@ test "calling an inferred async function" { } fn other() void { other_frame = @frame(); - suspend; + suspend {} x += 1; } }; @@ -129,7 +129,7 @@ test "@frameSize" { } fn other(param: i32) void { var local: i32 = undefined; - suspend; + suspend {} } }; S.doTheTest(); @@ -269,7 +269,7 @@ test "async function with dot syntax" { var y: i32 = 1; fn foo() callconv(.Async) void { y += 1; - suspend; + suspend {} } }; const p = async S.foo(); @@ -298,7 +298,7 @@ fn doTheAwait(f: anyframe->void) void { fn simpleAsyncFn2(y: *i32) callconv(.Async) void { defer y.* += 2; y.* += 1; - suspend; + suspend {} } test "@asyncCall with return type" { @@ -312,7 +312,7 @@ test "@asyncCall with return type" { fn afunc() i32 { global_frame = @frame(); - suspend; + suspend {} return 1234; } }; @@ -348,7 +348,7 @@ test "async fn with inferred error set" { fn failing() !void { global_frame = @frame(); - suspend; + suspend {} return error.Fail; } }; @@ -375,7 +375,7 @@ fn nonFailing() (anyframe->anyerror!void) { return &Static.frame; } fn suspendThenFail() callconv(.Async) anyerror!void { - suspend; + suspend {} return error.Fail; } fn printTrace(p: anyframe->(anyerror!void)) callconv(.Async) void { @@ -400,7 +400,7 @@ fn testBreakFromSuspend(my_result: *i32) callconv(.Async) void { resume @frame(); } my_result.* += 1; - suspend; + suspend {} my_result.* += 1; } @@ -421,7 +421,7 @@ test "heap allocated async function frame" { fn someFunc() void { x += 1; - suspend; + suspend {} x += 1; } }; @@ -454,7 +454,7 @@ test "async function call return value" { fn other(x: i32, y: i32) Point { frame = @frame(); - suspend; + suspend {} return Point{ .x = x, .y = y, @@ -487,7 +487,7 @@ test "suspension points inside branching control flow" { fn func(b: bool) void { while (b) { - suspend; + suspend {} result += 1; } } @@ -541,7 +541,7 @@ test "pass string literal to async function" { fn hello(msg: []const u8) void { frame = @frame(); - suspend; + suspend {} expectEqualStrings("hello", msg); ok = true; } @@ -566,7 +566,7 @@ test "await inside an errdefer" { fn func() void { frame = @frame(); - suspend; + suspend {} } }; S.doTheTest(); @@ -590,7 +590,7 @@ test "try in an async function with error union and non-zero-bit payload" { fn theProblem() ![]u8 { frame = @frame(); - suspend; + suspend {} const result = try other(); return result; } @@ -622,7 +622,7 @@ test "returning a const error from async function" { fn fetchUrl(unused: i32, url: []const u8) ![]u8 { frame = @frame(); - suspend; + suspend {} ok = true; return error.OutOfMemory; } @@ -967,7 +967,7 @@ test "@asyncCall with comptime-known function, but not awaited directly" { fn failing() !void { global_frame = @frame(); - suspend; + suspend {} return error.Fail; } }; @@ -977,7 +977,7 @@ test "@asyncCall with comptime-known function, but not awaited directly" { test "@asyncCall with actual frame instead of byte buffer" { const S = struct { fn func() i32 { - suspend; + suspend {} return 1234; } }; @@ -993,7 +993,7 @@ test "@asyncCall using the result location inside the frame" { fn simple2(y: *i32) callconv(.Async) i32 { defer y.* += 2; y.* += 1; - suspend; + suspend {} return 1234; } fn getAnswer(f: anyframe->i32, out: *i32) void { @@ -1095,7 +1095,7 @@ test "nosuspend function call" { } fn add(a: i32, b: i32) i32 { if (a > 100) { - suspend; + suspend {} } return a + b; } @@ -1170,7 +1170,7 @@ test "suspend in for loop" { global_frame = @frame(); var sum: u32 = 0; for (stuff) |x| { - suspend; + suspend {} sum += x; } global_frame = null; @@ -1197,7 +1197,7 @@ test "suspend in while loop" { global_frame = @frame(); defer global_frame = null; while (stuff) |val| { - suspend; + suspend {} return val; } return 0; @@ -1206,7 +1206,7 @@ test "suspend in while loop" { global_frame = @frame(); defer global_frame = null; while (stuff) |val| { - suspend; + suspend {} return val; } else |err| { return 0; @@ -1339,7 +1339,7 @@ test "async function passed 0-bit arg after non-0-bit arg" { fn bar(x: i32, args: anytype) anyerror!void { global_frame = @frame(); - suspend; + suspend {} global_int = x; } }; @@ -1361,7 +1361,7 @@ test "async function passed align(16) arg after align(8) arg" { fn bar(x: u64, args: anytype) anyerror!void { expect(x == 10); global_frame = @frame(); - suspend; + suspend {} global_int = args[0]; } }; @@ -1383,7 +1383,7 @@ test "async function call resolves target fn frame, comptime func" { fn bar() anyerror!void { global_frame = @frame(); - suspend; + suspend {} global_int += 1; } }; @@ -1406,7 +1406,7 @@ test "async function call resolves target fn frame, runtime func" { fn bar() anyerror!void { global_frame = @frame(); - suspend; + suspend {} global_int += 1; } }; @@ -1430,7 +1430,7 @@ test "properly spill optional payload capture value" { fn bar() void { global_frame = @frame(); - suspend; + suspend {} global_int += 1; } }; @@ -1466,13 +1466,13 @@ test "handle defer interfering with return value spill" { fn bar() anyerror!void { global_frame1 = @frame(); - suspend; + suspend {} return error.Bad; } fn baz() void { global_frame2 = @frame(); - suspend; + suspend {} baz_happened = true; } }; @@ -1497,7 +1497,7 @@ test "take address of temporary async frame" { fn foo(arg: i32) i32 { global_frame = @frame(); - suspend; + suspend {} return arg + 1234; } @@ -1520,7 +1520,7 @@ test "nosuspend await" { fn foo(want_suspend: bool) i32 { if (want_suspend) { - suspend; + suspend {} } return 42; } @@ -1569,11 +1569,11 @@ test "nosuspend on async function calls" { // }; // const S1 = struct { // fn c() S0 { -// suspend; +// suspend {} // return S0{}; // } // fn d() !S0 { -// suspend; +// suspend {} // return S0{}; // } // }; @@ -1591,11 +1591,11 @@ test "nosuspend resume async function calls" { }; const S1 = struct { fn c() S0 { - suspend; + suspend {} return S0{}; } fn d() !S0 { - suspend; + suspend {} return S0{}; } }; diff --git a/zig/test/stage1/behavior/math.zig b/zig/test/stage1/behavior/math.zig index 68690c9af8..32f4842702 100644 --- a/zig/test/stage1/behavior/math.zig +++ b/zig/test/stage1/behavior/math.zig @@ -229,16 +229,26 @@ fn testSignedWrappingEval(x: i32) void { expect(max_val == maxInt(i32)); } -test "negation wrapping" { - testNegationWrappingEval(minInt(i16)); - comptime testNegationWrappingEval(minInt(i16)); +test "signed negation wrapping" { + testSignedNegationWrappingEval(minInt(i16)); + comptime testSignedNegationWrappingEval(minInt(i16)); } -fn testNegationWrappingEval(x: i16) void { +fn testSignedNegationWrappingEval(x: i16) void { expect(x == -32768); const neg = -%x; expect(neg == -32768); } +test "unsigned negation wrapping" { + testUnsignedNegationWrappingEval(1); + comptime testUnsignedNegationWrappingEval(1); +} +fn testUnsignedNegationWrappingEval(x: u16) void { + expect(x == 1); + const neg = -%x; + expect(neg == maxInt(u16)); +} + test "unsigned 64-bit division" { test_u64_div(); comptime test_u64_div(); @@ -843,3 +853,20 @@ test "compare undefined literal with comptime_int" { x = true; expect(x); } + +test "signed zeros are represented properly" { + const S = struct { + fn doTheTest() void { + inline for ([_]type{ f16, f32, f64, f128 }) |T| { + const ST = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); + var as_fp_val = -@as(T, 0.0); + var as_uint_val = @bitCast(ST, as_fp_val); + // Ensure the sign bit is set. + expect(as_uint_val >> (@typeInfo(T).Float.bits - 1) == 1); + } + } + }; + + S.doTheTest(); + comptime S.doTheTest(); +} diff --git a/zig/test/stage1/behavior/type_info.zig b/zig/test/stage1/behavior/type_info.zig index 6dec7ca4d2..f944b7904c 100644 --- a/zig/test/stage1/behavior/type_info.zig +++ b/zig/test/stage1/behavior/type_info.zig @@ -306,7 +306,7 @@ test "type info: function type info" { fn testFunction() void { const fn_info = @typeInfo(@TypeOf(foo)); expect(fn_info == .Fn); - expect(fn_info.Fn.alignment == 0); + expect(fn_info.Fn.alignment > 0); expect(fn_info.Fn.calling_convention == .C); expect(!fn_info.Fn.is_generic); expect(fn_info.Fn.args.len == 2); diff --git a/zig/test/stage1/behavior/vector.zig b/zig/test/stage1/behavior/vector.zig index d3276496de..4b88ce020a 100644 --- a/zig/test/stage1/behavior/vector.zig +++ b/zig/test/stage1/behavior/vector.zig @@ -510,19 +510,6 @@ test "vector reduce operation" { const N = @typeInfo(@TypeOf(x)).Array.len; const TX = @typeInfo(@TypeOf(x)).Array.child; - // wasmtime: unknown import: `env::fminf` has not been defined - // https://github.com/ziglang/zig/issues/8131 - switch (std.builtin.arch) { - .wasm32 => switch (@typeInfo(TX)) { - .Float => switch (op) { - .Min, .Max, => return, - else => {}, - }, - else => {}, - }, - else => {}, - } - var r = @reduce(op, @as(Vector(N, TX), x)); switch (@typeInfo(TX)) { .Int, .Bool => expectEqual(expected, r), diff --git a/zig/test/stage2/riscv64.zig b/zig/test/stage2/riscv64.zig new file mode 100644 index 0000000000..e2035be47a --- /dev/null +++ b/zig/test/stage2/riscv64.zig @@ -0,0 +1,45 @@ +const std = @import("std"); +const TestContext = @import("../../src/test.zig").TestContext; + +const linux_riscv64 = std.zig.CrossTarget{ + .cpu_arch = .riscv64, + .os_tag = .linux, +}; + +pub fn addCases(ctx: *TestContext) !void { + { + var case = ctx.exe("riscv64 hello world", linux_riscv64); + // Regular old hello world + case.addCompareOutput( + \\export fn _start() noreturn { + \\ print(); + \\ + \\ exit(); + \\} + \\ + \\fn print() void { + \\ asm volatile ("ecall" + \\ : + \\ : [number] "{a7}" (64), + \\ [arg1] "{a0}" (1), + \\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")), + \\ [arg3] "{a2}" ("Hello, World!\n".len) + \\ : "rcx", "r11", "memory" + \\ ); + \\ return; + \\} + \\ + \\fn exit() noreturn { + \\ asm volatile ("ecall" + \\ : + \\ : [number] "{a7}" (94), + \\ [arg1] "{a0}" (0) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "Hello, World!\n", + ); + } +} diff --git a/zig/test/stage2/test.zig b/zig/test/stage2/test.zig index b4bc1a413e..c8b9b0cc96 100644 --- a/zig/test/stage2/test.zig +++ b/zig/test/stage2/test.zig @@ -11,11 +11,6 @@ const linux_x64 = std.zig.CrossTarget{ .os_tag = .linux, }; -const linux_riscv64 = std.zig.CrossTarget{ - .cpu_arch = .riscv64, - .os_tag = .linux, -}; - pub fn addCases(ctx: *TestContext) !void { try @import("cbe.zig").addCases(ctx); try @import("spu-ii.zig").addCases(ctx); @@ -24,6 +19,7 @@ pub fn addCases(ctx: *TestContext) !void { try @import("llvm.zig").addCases(ctx); try @import("wasm.zig").addCases(ctx); try @import("darwin.zig").addCases(ctx); + try @import("riscv64.zig").addCases(ctx); { var case = ctx.exe("hello world with updates", linux_x64); @@ -137,42 +133,6 @@ pub fn addCases(ctx: *TestContext) !void { ); } - { - var case = ctx.exe("riscv64 hello world", linux_riscv64); - // Regular old hello world - case.addCompareOutput( - \\export fn _start() noreturn { - \\ print(); - \\ - \\ exit(); - \\} - \\ - \\fn print() void { - \\ asm volatile ("ecall" - \\ : - \\ : [number] "{a7}" (64), - \\ [arg1] "{a0}" (1), - \\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{a2}" ("Hello, World!\n".len) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - \\ - \\fn exit() noreturn { - \\ asm volatile ("ecall" - \\ : - \\ : [number] "{a7}" (94), - \\ [arg1] "{a0}" (0) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "Hello, World!\n", - ); - } - { var case = ctx.exe("adding numbers at comptime", linux_x64); case.addCompareOutput( @@ -1048,7 +1008,7 @@ pub fn addCases(ctx: *TestContext) !void { "Hello, World!\n", ); try case.files.append(.{ - .src = + .src = \\pub fn print() void { \\ asm volatile ("syscall" \\ : @@ -1085,7 +1045,7 @@ pub fn addCases(ctx: *TestContext) !void { &.{":2:25: error: 'print' is private"}, ); try case.files.append(.{ - .src = + .src = \\fn print() void { \\ asm volatile ("syscall" \\ : diff --git a/zig/test/tests.zig b/zig/test/tests.zig index be8d8d0a3f..7cdf83f2cd 100644 --- a/zig/test/tests.zig +++ b/zig/test/tests.zig @@ -212,6 +212,22 @@ const test_targets = blk: { // .link_libc = true, //}, + TestTarget{ + .target = .{ + .cpu_arch = .powerpc, + .os_tag = .linux, + .abi = .none, + }, + }, + TestTarget{ + .target = .{ + .cpu_arch = .powerpc, + .os_tag = .linux, + .abi = .musl, + }, + .link_libc = true, + }, + TestTarget{ .target = .{ .cpu_arch = .riscv64, diff --git a/zig/test/translate_c.zig b/zig/test/translate_c.zig index 142579c92c..4021210b29 100644 --- a/zig/test/translate_c.zig +++ b/zig/test/translate_c.zig @@ -3,6 +3,22 @@ const std = @import("std"); const CrossTarget = std.zig.CrossTarget; pub fn addCases(cases: *tests.TranslateCContext) void { + cases.add("field access is grouped if necessary", + \\unsigned long foo(unsigned long x) { + \\ return ((union{unsigned long _x}){x})._x; + \\} + , &[_][]const u8{ + \\pub export fn foo(arg_x: c_ulong) c_ulong { + \\ var x = arg_x; + \\ const union_unnamed_1 = extern union { + \\ _x: c_ulong, + \\ }; + \\ return (union_unnamed_1{ + \\ ._x = x, + \\ })._x; + \\} + }); + cases.add("unnamed child types of typedef receive typedef's name", \\typedef enum { \\ FooA, @@ -111,7 +127,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ const A = @enumToInt(enum_Foo.A); \\ const B = @enumToInt(enum_Foo.B); \\ const C = @enumToInt(enum_Foo.C); - \\ var a: enum_Foo = @intToEnum(enum_Foo, B); + \\ var a: enum_Foo = @import("std").meta.cast(enum_Foo, B); \\ { \\ const enum_Foo = extern enum(c_int) { \\ A, @@ -122,7 +138,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ const A_2 = @enumToInt(enum_Foo.A); \\ const B_3 = @enumToInt(enum_Foo.B); \\ const C_4 = @enumToInt(enum_Foo.C); - \\ var a_5: enum_Foo = @intToEnum(enum_Foo, B_3); + \\ var a_5: enum_Foo = @import("std").meta.cast(enum_Foo, B_3); \\ } \\} }); @@ -1347,7 +1363,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub export fn ptrcast() [*c]f32 { \\ var a: [*c]c_int = undefined; - \\ return @ptrCast([*c]f32, @alignCast(@alignOf(f32), a)); + \\ return @ptrCast([*c]f32, @alignCast(@import("std").meta.alignment(f32), a)); \\} }); @@ -1371,16 +1387,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub export fn test_ptr_cast() void { \\ var p: ?*c_void = undefined; \\ { - \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p)); - \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p)); - \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p)); - \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p)); + \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@import("std").meta.alignment(u8), p)); + \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@import("std").meta.alignment(c_short), p)); + \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@import("std").meta.alignment(c_int), p)); + \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@import("std").meta.alignment(c_longlong), p)); \\ } \\ { - \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p)); - \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p)); - \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p)); - \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p)); + \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@import("std").meta.alignment(u8), p)); + \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@import("std").meta.alignment(c_short), p)); + \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@import("std").meta.alignment(c_int), p)); + \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@import("std").meta.alignment(c_longlong), p)); \\ } \\} }); @@ -1676,7 +1692,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const e = @enumToInt(enum_unnamed_1.e); \\pub const f = @enumToInt(enum_unnamed_1.f); \\pub const g = @enumToInt(enum_unnamed_1.g); - \\pub export var h: enum_unnamed_1 = @intToEnum(enum_unnamed_1, e); + \\pub export var h: enum_unnamed_1 = @import("std").meta.cast(enum_unnamed_1, e); \\const enum_unnamed_2 = extern enum(c_int) { \\ i, \\ j, @@ -2308,7 +2324,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ var a = arg_a; \\ var b = arg_b; \\ var c = arg_c; - \\ var d: enum_Foo = @intToEnum(enum_Foo, FooA); + \\ var d: enum_Foo = @import("std").meta.cast(enum_Foo, FooA); \\ var e: c_int = @boolToInt((a != 0) and (b != 0)); \\ var f: c_int = @boolToInt((b != 0) and (c != null)); \\ var g: c_int = @boolToInt((a != 0) and (c != null)); @@ -3012,7 +3028,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\void call() { \\ fn_int(3.0f); \\ fn_int(3.0); - \\ fn_int(3.0L); \\ fn_int('ABCD'); \\ fn_f32(3); \\ fn_f64(3); @@ -3037,7 +3052,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub export fn call() void { \\ fn_int(@floatToInt(c_int, 3.0)); \\ fn_int(@floatToInt(c_int, 3.0)); - \\ fn_int(@floatToInt(c_int, 3.0)); \\ fn_int(@as(c_int, 1094861636)); \\ fn_f32(@intToFloat(f32, @as(c_int, 3))); \\ fn_f64(@intToFloat(f64, @as(c_int, 3))); diff --git a/zig/tools/update_cpu_features.zig b/zig/tools/update_cpu_features.zig index 7e3c636c31..c2ddd87f7c 100644 --- a/zig/tools/update_cpu_features.zig +++ b/zig/tools/update_cpu_features.zig @@ -239,6 +239,28 @@ const llvm_targets = [_]LlvmTarget{ "zcz_fp", }, }, + .{ + .llvm_name = null, + .zig_name = "xgene1", + .features = &.{ + "fp_armv8", + "neon", + "perfmon", + "v8a", + }, + }, + .{ + .llvm_name = null, + .zig_name = "emag", + .features = &.{ + "crc", + "crypto", + "fp_armv8", + "neon", + "perfmon", + "v8a", + }, + }, }, }, .{ @@ -663,6 +685,12 @@ const llvm_targets = [_]LlvmTarget{ .zig_name = "powerpc", .llvm_name = "PowerPC", .td_name = "PPC.td", + .feature_overrides = &.{ + .{ + .llvm_name = "ppc32", + .omit = true, + }, + }, }, .{ .zig_name = "riscv",