diff --git a/CMakeLists.txt b/CMakeLists.txt index d1b23d79c6..ef9b83b899 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,11 +51,11 @@ message("Configuring zig version ${ZIG_VERSION}") set(ZIG_STATIC off CACHE BOOL "Attempt to build a static zig executable (not compatible with glibc)") set(ZIG_STATIC_LLVM off CACHE BOOL "Prefer linking against static LLVM libraries") -set(ZIG_ENABLE_MEM_PROFILE off CACHE BOOL "Activate memory usage instrumentation") set(ZIG_PREFER_CLANG_CPP_DYLIB off CACHE BOOL "Try to link against -lclang-cpp") set(ZIG_WORKAROUND_4799 off CACHE BOOL "workaround for https://github.com/ziglang/zig/issues/4799") set(ZIG_WORKAROUND_POLLY_SO off CACHE STRING "workaround for https://github.com/ziglang/zig/issues/4799") set(ZIG_USE_CCACHE off CACHE BOOL "Use ccache if available") +set(ZIG_WORKAROUND_6087 off CACHE BOOL "workaround for https://github.com/ziglang/zig/issues/6087") if(CCACHE_PROGRAM AND ZIG_USE_CCACHE) SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}") @@ -71,11 +71,6 @@ string(REGEX REPLACE "\\\\" "\\\\\\\\" ZIG_LIBC_INCLUDE_DIR_ESCAPED "${ZIG_LIBC_ option(ZIG_TEST_COVERAGE "Build Zig with test coverage instrumentation" OFF) -# Zig no longer has embedded LLD. This option is kept for package maintainers -# so that they don't have to update their scripts in case we ever re-introduce -# LLD to the tree. This option does nothing. -option(ZIG_FORCE_EXTERNAL_LLD "does nothing" OFF) - set(ZIG_TARGET_TRIPLE "native" CACHE STRING "arch-os-abi to output binaries for") set(ZIG_TARGET_MCPU "baseline" CACHE STRING "-mcpu parameter to output binaries for") set(ZIG_EXECUTABLE "" CACHE STRING "(when cross compiling) path to already-built zig binary") @@ -90,12 +85,17 @@ if(APPLE AND ZIG_STATIC) list(APPEND LLVM_LIBRARIES "${ZLIB}") endif() +if(APPLE AND ZIG_WORKAROUND_6087) + list(REMOVE_ITEM LLVM_LIBRARIES "-llibxml2.tbd") + list(APPEND LLVM_LIBRARIES "-lxml2") +endif() + if(APPLE AND ZIG_WORKAROUND_4799) # eg: ${CMAKE_PREFIX_PATH} could be /usr/local/opt/llvm/ list(APPEND LLVM_LIBRARIES "-Wl,${CMAKE_PREFIX_PATH}/lib/libPolly.a" "-Wl,${CMAKE_PREFIX_PATH}/lib/libPollyPPCG.a" "-Wl,${CMAKE_PREFIX_PATH}/lib/libPollyISL.a") endif() -set(ZIG_CPP_LIB_DIR "${CMAKE_BINARY_DIR}/zig_cpp") +set(ZIG_CPP_LIB_DIR "${CMAKE_BINARY_DIR}/zigcpp") # Handle multi-config builds and place each into a common lib. The VS generator # for example will append a Debug folder by default if not explicitly specified. @@ -261,53 +261,45 @@ include_directories("${CMAKE_SOURCE_DIR}/deps/dbg-macro") find_package(Threads) -# CMake doesn't let us create an empty executable, so we hang on to this one separately. -set(ZIG_MAIN_SRC "${CMAKE_SOURCE_DIR}/src/main.cpp") +# This is our shim which will be replaced by stage1.zig. +set(ZIG0_SOURCES + "${CMAKE_SOURCE_DIR}/src/stage1/zig0.cpp" +) -# This is our shim which will be replaced by libstage2 written in Zig. -set(ZIG0_SHIM_SRC "${CMAKE_SOURCE_DIR}/src/stage2.cpp") - -if(ZIG_ENABLE_MEM_PROFILE) - set(ZIG_SOURCES_MEM_PROFILE "${CMAKE_SOURCE_DIR}/src/mem_profile.cpp") -endif() - -set(ZIG_SOURCES - "${CMAKE_SOURCE_DIR}/src/analyze.cpp" - "${CMAKE_SOURCE_DIR}/src/ast_render.cpp" - "${CMAKE_SOURCE_DIR}/src/bigfloat.cpp" - "${CMAKE_SOURCE_DIR}/src/bigint.cpp" - "${CMAKE_SOURCE_DIR}/src/buffer.cpp" - "${CMAKE_SOURCE_DIR}/src/cache_hash.cpp" - "${CMAKE_SOURCE_DIR}/src/codegen.cpp" - "${CMAKE_SOURCE_DIR}/src/compiler.cpp" - "${CMAKE_SOURCE_DIR}/src/dump_analysis.cpp" - "${CMAKE_SOURCE_DIR}/src/errmsg.cpp" - "${CMAKE_SOURCE_DIR}/src/error.cpp" - "${CMAKE_SOURCE_DIR}/src/glibc.cpp" - "${CMAKE_SOURCE_DIR}/src/heap.cpp" - "${CMAKE_SOURCE_DIR}/src/ir.cpp" - "${CMAKE_SOURCE_DIR}/src/ir_print.cpp" - "${CMAKE_SOURCE_DIR}/src/link.cpp" - "${CMAKE_SOURCE_DIR}/src/mem.cpp" - "${CMAKE_SOURCE_DIR}/src/os.cpp" - "${CMAKE_SOURCE_DIR}/src/parser.cpp" - "${CMAKE_SOURCE_DIR}/src/range_set.cpp" - "${CMAKE_SOURCE_DIR}/src/target.cpp" - "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp" - "${CMAKE_SOURCE_DIR}/src/util.cpp" - "${CMAKE_SOURCE_DIR}/src/softfloat_ext.cpp" - "${ZIG_SOURCES_MEM_PROFILE}" +set(STAGE1_SOURCES + "${CMAKE_SOURCE_DIR}/src/stage1/analyze.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/ast_render.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/bigfloat.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/bigint.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/buffer.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/codegen.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/dump_analysis.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/errmsg.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/error.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/heap.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/ir.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/ir_print.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/mem.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/os.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/parser.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/range_set.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/stage1.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/target.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/tokenizer.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/util.cpp" + "${CMAKE_SOURCE_DIR}/src/stage1/softfloat_ext.cpp" ) set(OPTIMIZED_C_SOURCES - "${CMAKE_SOURCE_DIR}/src/blake2b.c" - "${CMAKE_SOURCE_DIR}/src/parse_f128.c" + "${CMAKE_SOURCE_DIR}/src/stage1/parse_f128.c" ) set(ZIG_CPP_SOURCES + # These are planned to stay even when we are self-hosted. "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp" "${CMAKE_SOURCE_DIR}/src/zig_clang.cpp" "${CMAKE_SOURCE_DIR}/src/zig_clang_driver.cpp" "${CMAKE_SOURCE_DIR}/src/zig_clang_cc1_main.cpp" "${CMAKE_SOURCE_DIR}/src/zig_clang_cc1as_main.cpp" + # https://github.com/ziglang/zig/issues/6363 "${CMAKE_SOURCE_DIR}/src/windows_sdk.cpp" ) @@ -328,7 +320,7 @@ set(ZIG_STD_DEST "${ZIG_LIB_DIR}/std") set(ZIG_CONFIG_H_OUT "${CMAKE_BINARY_DIR}/config.h") set(ZIG_CONFIG_ZIG_OUT "${CMAKE_BINARY_DIR}/config.zig") configure_file ( - "${CMAKE_SOURCE_DIR}/src/config.h.in" + "${CMAKE_SOURCE_DIR}/src/stage1/config.h.in" "${ZIG_CONFIG_H_OUT}" ) configure_file ( @@ -340,6 +332,7 @@ include_directories( ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} "${CMAKE_SOURCE_DIR}/src" + "${CMAKE_SOURCE_DIR}/src/stage1" ) # These have to go before the -Wno- flags @@ -405,18 +398,19 @@ if(ZIG_TEST_COVERAGE) set(EXE_LDFLAGS "${EXE_LDFLAGS} -fprofile-arcs -ftest-coverage") endif() -add_library(zig_cpp STATIC ${ZIG_CPP_SOURCES}) -set_target_properties(zig_cpp PROPERTIES +add_library(zigcpp STATIC ${ZIG_CPP_SOURCES}) +set_target_properties(zigcpp PROPERTIES COMPILE_FLAGS ${EXE_CFLAGS} ) -target_link_libraries(zig_cpp LINK_PUBLIC +target_link_libraries(zigcpp LINK_PUBLIC ${CLANG_LIBRARIES} ${LLD_LIBRARIES} ${LLVM_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} ) if(ZIG_WORKAROUND_POLLY_SO) - target_link_libraries(zig_cpp LINK_PUBLIC "-Wl,${ZIG_WORKAROUND_POLLY_SO}") + target_link_libraries(zigcpp LINK_PUBLIC "-Wl,${ZIG_WORKAROUND_POLLY_SO}") endif() add_library(opt_c_util STATIC ${OPTIMIZED_C_SOURCES}) @@ -424,68 +418,67 @@ set_target_properties(opt_c_util PROPERTIES COMPILE_FLAGS "${OPTIMIZED_C_FLAGS}" ) -add_library(zigcompiler STATIC ${ZIG_SOURCES}) -set_target_properties(zigcompiler PROPERTIES +add_library(zigstage1 STATIC ${STAGE1_SOURCES}) +set_target_properties(zigstage1 PROPERTIES COMPILE_FLAGS ${EXE_CFLAGS} LINK_FLAGS ${EXE_LDFLAGS} ) -target_link_libraries(zigcompiler LINK_PUBLIC - zig_cpp +target_link_libraries(zigstage1 LINK_PUBLIC opt_c_util ${SOFTFLOAT_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} + zigcpp ) if(NOT MSVC) - target_link_libraries(zigcompiler LINK_PUBLIC ${LIBXML2}) + target_link_libraries(zigstage1 LINK_PUBLIC ${LIBXML2}) endif() if(ZIG_DIA_GUIDS_LIB) - target_link_libraries(zigcompiler LINK_PUBLIC ${ZIG_DIA_GUIDS_LIB}) + target_link_libraries(zigstage1 LINK_PUBLIC ${ZIG_DIA_GUIDS_LIB}) endif() if(MSVC OR MINGW) - target_link_libraries(zigcompiler LINK_PUBLIC version) + target_link_libraries(zigstage1 LINK_PUBLIC version) endif() -add_executable(zig0 "${ZIG_MAIN_SRC}" "${ZIG0_SHIM_SRC}") +add_executable(zig0 ${ZIG0_SOURCES}) set_target_properties(zig0 PROPERTIES COMPILE_FLAGS ${EXE_CFLAGS} LINK_FLAGS ${EXE_LDFLAGS} ) -target_link_libraries(zig0 zigcompiler) +target_link_libraries(zig0 zigstage1) if(MSVC) - set(LIBSTAGE2 "${CMAKE_BINARY_DIR}/zigstage2.lib") + set(ZIG1_OBJECT "${CMAKE_BINARY_DIR}/zig1.obj") else() - set(LIBSTAGE2 "${CMAKE_BINARY_DIR}/libzigstage2.a") + set(ZIG1_OBJECT "${CMAKE_BINARY_DIR}/zig1.o") endif() if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - set(LIBSTAGE2_RELEASE_ARG "") + set(ZIG1_RELEASE_ARG "") else() - set(LIBSTAGE2_RELEASE_ARG --release-fast --strip) + set(ZIG1_RELEASE_ARG -OReleaseFast --strip) endif() -set(BUILD_LIBSTAGE2_ARGS "build-lib" - "src-self-hosted/stage2.zig" +set(BUILD_ZIG1_ARGS + "src/stage1.zig" -target "${ZIG_TARGET_TRIPLE}" "-mcpu=${ZIG_TARGET_MCPU}" - --name zigstage2 + --name zig1 --override-lib-dir "${CMAKE_SOURCE_DIR}/lib" - --cache on - --output-dir "${CMAKE_BINARY_DIR}" - ${LIBSTAGE2_RELEASE_ARG} - --bundle-compiler-rt - -fPIC + "-femit-bin=${ZIG1_OBJECT}" + "${ZIG1_RELEASE_ARG}" -lc --pkg-begin build_options "${ZIG_CONFIG_ZIG_OUT}" --pkg-end + --pkg-begin compiler_rt "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt.zig" + --pkg-end ) if("${ZIG_TARGET_TRIPLE}" STREQUAL "native") - add_custom_target(zig_build_libstage2 ALL - COMMAND zig0 ${BUILD_LIBSTAGE2_ARGS} + add_custom_target(zig_build_zig1 ALL + COMMAND zig0 ${BUILD_ZIG1_ARGS} DEPENDS zig0 - BYPRODUCTS "${LIBSTAGE2}" + BYPRODUCTS "${ZIG1_OBJECT}" + COMMENT STATUS "Building self-hosted component ${ZIG1_OBJECT}" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" ) set(ZIG_EXECUTABLE "${zig_BINARY_DIR}/zig") @@ -493,26 +486,28 @@ if("${ZIG_TARGET_TRIPLE}" STREQUAL "native") set(ZIG_EXECUTABLE "${ZIG_EXECUTABLE}.exe") endif() else() - add_custom_target(zig_build_libstage2 ALL - COMMAND "${ZIG_EXECUTABLE}" ${BUILD_LIBSTAGE2_ARGS} - BYPRODUCTS "${LIBSTAGE2}" + add_custom_target(zig_build_zig1 ALL + COMMAND "${ZIG_EXECUTABLE}" ${BUILD_ZIG1_ARGS} + BYPRODUCTS "${ZIG1_OBJECT}" + COMMENT STATUS "Building self-hosted component ${ZIG1_OBJECT}" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" ) endif() -add_executable(zig "${ZIG_MAIN_SRC}") +# cmake won't let us configure an executable without C sources. +add_executable(zig "${CMAKE_SOURCE_DIR}/src/stage1/empty.cpp") set_target_properties(zig PROPERTIES COMPILE_FLAGS ${EXE_CFLAGS} LINK_FLAGS ${EXE_LDFLAGS} ) -target_link_libraries(zig zigcompiler "${LIBSTAGE2}") +target_link_libraries(zig "${ZIG1_OBJECT}" zigstage1) if(MSVC) target_link_libraries(zig ntdll.lib) elseif(MINGW) target_link_libraries(zig ntdll) endif() -add_dependencies(zig zig_build_libstage2) +add_dependencies(zig zig_build_zig1) install(TARGETS zig DESTINATION bin) diff --git a/README.md b/README.md index 021a6b6d13..80ebd8955c 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ Note that you can ### Stage 1: Build Zig from C++ Source Code +This step must be repeated when you make changes to any of the C++ source code. + #### Dependencies ##### POSIX @@ -69,6 +71,41 @@ make install See https://github.com/ziglang/zig/wiki/Building-Zig-on-Windows +### Stage 2: Build Self-Hosted Zig from Zig Source Code + +Now we use the stage1 binary: + +``` +zig build --prefix $(pwd)/stage2 -Denable-llvm +``` + +This produces `stage2/bin/zig` which can be used for testing and development. +Once it is feature complete, it will be used to build stage 3 - the final compiler +binary. + +### Stage 3: Rebuild Self-Hosted Zig Using the Self-Hosted Compiler + +*Note: Stage 2 compiler is not yet able to build Stage 3. Building Stage 3 is +not yet supported.* + +Once the self-hosted compiler can build itself, this will be the actual +compiler binary that we will install to the system. Until then, users should +use stage 1. + +#### Debug / Development Build + +``` +stage2/bin/zig build +``` + +This produces `zig-cache/bin/zig`. + +#### Release / Install Build + +``` +stage2/bin/zig build install -Drelease +``` + ## License The ultimate goal of the Zig project is to serve users. As a first-order diff --git a/build.zig b/build.zig index a6a2d87371..b4d8b71f79 100644 --- a/build.zig +++ b/build.zig @@ -9,6 +9,7 @@ const ArrayList = std.ArrayList; const io = std.io; const fs = std.fs; const InstallDirectoryOptions = std.build.InstallDirectoryOptions; +const assert = std.debug.assert; const zig_version = std.builtin.Version{ .major = 0, .minor = 6, .patch = 0 }; @@ -37,7 +38,7 @@ pub fn build(b: *Builder) !void { const test_step = b.step("test", "Run all the tests"); - var test_stage2 = b.addTest("src-self-hosted/test.zig"); + var test_stage2 = b.addTest("src/test.zig"); test_stage2.setBuildMode(mode); test_stage2.addPackagePath("stage2_tests", "test/stage2/test.zig"); @@ -55,70 +56,6 @@ pub fn build(b: *Builder) !void { const enable_llvm = b.option(bool, "enable-llvm", "Build self-hosted compiler with LLVM backend enabled") orelse false; const config_h_path_option = b.option([]const u8, "config_h", "Path to the generated config.h"); - if (!only_install_lib_files) { - var exe = b.addExecutable("zig", "src-self-hosted/main.zig"); - exe.setBuildMode(mode); - exe.setTarget(target); - test_step.dependOn(&exe.step); - b.default_step.dependOn(&exe.step); - - if (enable_llvm) { - const config_h_text = if (config_h_path_option) |config_h_path| - try std.fs.cwd().readFileAlloc(b.allocator, toNativePathSep(b, config_h_path), max_config_h_bytes) - else - try findAndReadConfigH(b); - - var ctx = parseConfigH(b, config_h_text); - ctx.llvm = try findLLVM(b, ctx.llvm_config_exe); - - try configureStage2(b, exe, ctx); - } - if (!only_install_lib_files) { - exe.install(); - } - const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source"); - const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false; - if (link_libc) { - exe.linkLibC(); - test_stage2.linkLibC(); - } - - const log_scopes = b.option([]const []const u8, "log", "Which log scopes to enable") orelse &[0][]const u8{}; - const zir_dumps = b.option([]const []const u8, "dump-zir", "Which functions to dump ZIR for before codegen") orelse &[0][]const u8{}; - - const opt_version_string = b.option([]const u8, "version-string", "Override Zig version string. Default is to find out with git."); - const version = if (opt_version_string) |version| version else v: { - var code: u8 = undefined; - const version_untrimmed = b.execAllowFail(&[_][]const u8{ - "git", "-C", b.build_root, "name-rev", "HEAD", - "--tags", "--name-only", "--no-undefined", "--always", - }, &code, .Ignore) catch |err| { - std.debug.print( - \\Unable to determine zig version string: {} - \\Provide the zig version string explicitly using the `version-string` build option. - , .{err}); - std.process.exit(1); - }; - const trimmed = mem.trim(u8, version_untrimmed, " \n\r"); - break :v b.fmt("{}.{}.{}+{}", .{ zig_version.major, zig_version.minor, zig_version.patch, trimmed }); - }; - exe.addBuildOption([]const u8, "version", version); - - exe.addBuildOption([]const []const u8, "log_scopes", log_scopes); - exe.addBuildOption([]const []const u8, "zir_dumps", zir_dumps); - exe.addBuildOption(bool, "enable_tracy", tracy != null); - if (tracy) |tracy_path| { - const client_cpp = fs.path.join( - b.allocator, - &[_][]const u8{ tracy_path, "TracyClient.cpp" }, - ) catch unreachable; - exe.addIncludeDir(tracy_path); - exe.addCSourceFile(client_cpp, &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" }); - exe.linkSystemLibraryName("c++"); - exe.linkLibC(); - } - } - b.installDirectory(InstallDirectoryOptions{ .source_dir = "lib", .install_dir = .Lib, @@ -133,6 +70,95 @@ pub fn build(b: *Builder) !void { }, }); + if (only_install_lib_files) + return; + + const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source"); + const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse enable_llvm; + + var exe = b.addExecutable("zig", "src/main.zig"); + exe.install(); + exe.setBuildMode(mode); + exe.setTarget(target); + test_step.dependOn(&exe.step); + b.default_step.dependOn(&exe.step); + + exe.addBuildOption(bool, "have_llvm", enable_llvm); + if (enable_llvm) { + const config_h_text = if (config_h_path_option) |config_h_path| + try std.fs.cwd().readFileAlloc(b.allocator, toNativePathSep(b, config_h_path), max_config_h_bytes) + else + try findAndReadConfigH(b); + + var ctx = parseConfigH(b, config_h_text); + ctx.llvm = try findLLVM(b, ctx.llvm_config_exe); + + try configureStage2(b, exe, ctx, tracy != null); + } + if (link_libc) { + exe.linkLibC(); + test_stage2.linkLibC(); + } + + const log_scopes = b.option([]const []const u8, "log", "Which log scopes to enable") orelse &[0][]const u8{}; + const zir_dumps = b.option([]const []const u8, "dump-zir", "Which functions to dump ZIR for before codegen") orelse &[0][]const u8{}; + + const opt_version_string = b.option([]const u8, "version-string", "Override Zig version string. Default is to find out with git."); + const version = if (opt_version_string) |version| version else v: { + const version_string = b.fmt("{}.{}.{}", .{ zig_version.major, zig_version.minor, zig_version.patch }); + + var code: u8 = undefined; + const git_sha_untrimmed = b.execAllowFail(&[_][]const u8{ + "git", "-C", b.build_root, "name-rev", "HEAD", + "--tags", "--name-only", "--no-undefined", "--always", + }, &code, .Ignore) catch { + break :v version_string; + }; + const git_sha_trimmed = mem.trim(u8, git_sha_untrimmed, " \n\r"); + // Detect dirty changes. + const diff_untrimmed = b.execAllowFail(&[_][]const u8{ + "git", "-C", b.build_root, "diff", "HEAD", + }, &code, .Ignore) catch |err| { + std.debug.print("Error executing git diff: {}", .{err}); + std.process.exit(1); + }; + const trimmed_diff = mem.trim(u8, diff_untrimmed, " \n\r"); + const dirty_suffix = if (trimmed_diff.len == 0) "" else s: { + const dirty_hash = std.hash.Wyhash.hash(0, trimmed_diff); + break :s b.fmt("dirty{x}", .{@truncate(u32, dirty_hash)}); + }; + + // This will look like e.g. "0.6.0^0" for a tag commit. + if (mem.endsWith(u8, git_sha_trimmed, "^0")) { + const git_ver_string = git_sha_trimmed[0 .. git_sha_trimmed.len - 2]; + if (!mem.eql(u8, git_ver_string, version_string)) { + std.debug.print("Expected git tag '{}', found '{}'", .{ version_string, git_ver_string }); + std.process.exit(1); + } + break :v b.fmt("{}{}", .{ version_string, dirty_suffix }); + } else { + break :v b.fmt("{}+{}{}", .{ version_string, git_sha_trimmed, dirty_suffix }); + } + }; + exe.addBuildOption([]const u8, "version", version); + + exe.addBuildOption([]const []const u8, "log_scopes", log_scopes); + exe.addBuildOption([]const []const u8, "zir_dumps", zir_dumps); + exe.addBuildOption(bool, "enable_tracy", tracy != null); + exe.addBuildOption(bool, "is_stage1", false); + if (tracy) |tracy_path| { + const client_cpp = fs.path.join( + b.allocator, + &[_][]const u8{ tracy_path, "TracyClient.cpp" }, + ) catch unreachable; + exe.addIncludeDir(tracy_path); + exe.addCSourceFile(client_cpp, &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" }); + if (!enable_llvm) { + exe.linkSystemLibraryName("c++"); + } + exe.linkLibC(); + } + const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter"); const is_wine_enabled = b.option(bool, "enable-wine", "Use Wine to run cross compiled Windows tests") orelse false; @@ -140,10 +166,13 @@ pub fn build(b: *Builder) !void { const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false; const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc"); + test_stage2.addBuildOption(bool, "is_stage1", false); + 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); test_stage2.addBuildOption(bool, "enable_wasmtime", is_wasmtime_enabled); test_stage2.addBuildOption(?[]const u8, "glibc_multi_install_dir", glibc_multi_dir); + test_stage2.addBuildOption([]const u8, "version", version); const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests"); test_stage2_step.dependOn(&test_stage2.step); @@ -182,10 +211,7 @@ pub fn build(b: *Builder) !void { 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)); - const test_cli = tests.addCliTests(b, test_filter, modes); - const test_cli_step = b.step("test-cli", "Run zig cli tests"); - test_cli_step.dependOn(test_cli); - test_step.dependOn(test_cli); + 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)); @@ -241,7 +267,7 @@ fn fileExists(filename: []const u8) !bool { fn addCppLib(b: *Builder, lib_exe_obj: anytype, cmake_binary_dir: []const u8, lib_name: []const u8) void { lib_exe_obj.addObjectFile(fs.path.join(b.allocator, &[_][]const u8{ cmake_binary_dir, - "zig_cpp", + "zigcpp", b.fmt("{}{}{}", .{ lib_exe_obj.target.libPrefix(), lib_name, lib_exe_obj.target.staticLibSuffix() }), }) catch unreachable); } @@ -320,21 +346,17 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep { return result; } -fn configureStage2(b: *Builder, exe: anytype, ctx: Context) !void { +fn configureStage2(b: *Builder, exe: anytype, ctx: Context, need_cpp_includes: bool) !void { exe.addIncludeDir("src"); exe.addIncludeDir(ctx.cmake_binary_dir); - addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp"); - if (ctx.lld_include_dir.len != 0) { - exe.addIncludeDir(ctx.lld_include_dir); + addCppLib(b, exe, ctx.cmake_binary_dir, "zigcpp"); + assert(ctx.lld_include_dir.len != 0); + exe.addIncludeDir(ctx.lld_include_dir); + { var it = mem.tokenize(ctx.lld_libraries, ";"); while (it.next()) |lib| { exe.addObjectFile(lib); } - } else { - addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_wasm"); - addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_elf"); - addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_coff"); - addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_lib"); } { var it = mem.tokenize(ctx.clang_libraries, ";"); @@ -344,42 +366,51 @@ fn configureStage2(b: *Builder, exe: anytype, ctx: Context) !void { } dependOnLib(b, exe, ctx.llvm); - if (exe.target.getOsTag() == .linux) { - // First we try to static link against gcc libstdc++. If that doesn't work, - // we fall back to -lc++ and cross our fingers. - addCxxKnownPath(b, ctx, exe, "libstdc++.a", "") catch |err| switch (err) { - error.RequiredLibraryNotFound => { - exe.linkSystemLibrary("c++"); - }, - else => |e| return e, - }; + // Boy, it sure would be nice to simply linkSystemLibrary("c++") and rely on zig's + // ability to provide libc++ right? Well thanks to C++ not having a stable ABI this + // will cause linker errors. It would work in the situation when `zig cc` is used to + // build LLVM, Clang, and LLD, however when depending on them as system libraries, system + // libc++ must be used. + const cross_compile = false; // TODO + if (cross_compile) { + // In this case we assume that zig cc was used to build the LLVM, Clang, LLD dependencies. + exe.linkSystemLibrary("c++"); + } else { + if (exe.target.getOsTag() == .linux) { + // First we try to static link against gcc libstdc++. If that doesn't work, + // we fall back to -lc++ and cross our fingers. + addCxxKnownPath(b, ctx, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) { + error.RequiredLibraryNotFound => { + exe.linkSystemLibrary("c++"); + }, + else => |e| return e, + }; - exe.linkSystemLibrary("pthread"); - } else if (exe.target.isFreeBSD()) { - try addCxxKnownPath(b, ctx, exe, "libc++.a", null); - exe.linkSystemLibrary("pthread"); - } else if (exe.target.isDarwin()) { - if (addCxxKnownPath(b, ctx, exe, "libgcc_eh.a", "")) { - // Compiler is GCC. - try addCxxKnownPath(b, ctx, exe, "libstdc++.a", null); exe.linkSystemLibrary("pthread"); - // TODO LLD cannot perform this link. - // See https://github.com/ziglang/zig/issues/1535 - exe.enableSystemLinkerHack(); - } else |err| switch (err) { - error.RequiredLibraryNotFound => { - // System compiler, not gcc. - exe.linkSystemLibrary("c++"); - }, - else => |e| return e, + } else if (exe.target.isFreeBSD()) { + try addCxxKnownPath(b, ctx, exe, "libc++.a", null, need_cpp_includes); + exe.linkSystemLibrary("pthread"); + } else if (exe.target.isDarwin()) { + if (addCxxKnownPath(b, ctx, exe, "libgcc_eh.a", "", need_cpp_includes)) { + // Compiler is GCC. + try addCxxKnownPath(b, ctx, exe, "libstdc++.a", null, need_cpp_includes); + exe.linkSystemLibrary("pthread"); + // TODO LLD cannot perform this link. + // See https://github.com/ziglang/zig/issues/1535 + exe.enableSystemLinkerHack(); + } else |err| switch (err) { + error.RequiredLibraryNotFound => { + // System compiler, not gcc. + exe.linkSystemLibrary("c++"); + }, + else => |e| return e, + } + } + + if (ctx.dia_guids_lib.len != 0) { + exe.addObjectFile(ctx.dia_guids_lib); } } - - if (ctx.dia_guids_lib.len != 0) { - exe.addObjectFile(ctx.dia_guids_lib); - } - - exe.linkSystemLibrary("c"); } fn addCxxKnownPath( @@ -388,6 +419,7 @@ fn addCxxKnownPath( exe: anytype, objname: []const u8, errtxt: ?[]const u8, + need_cpp_includes: bool, ) !void { const path_padded = try b.exec(&[_][]const u8{ ctx.cxx_compiler, @@ -403,6 +435,16 @@ fn addCxxKnownPath( return error.RequiredLibraryNotFound; } exe.addObjectFile(path_unpadded); + + // TODO a way to integrate with system c++ include files here + // cc -E -Wp,-v -xc++ /dev/null + if (need_cpp_includes) { + // I used these temporarily for testing something but we obviously need a + // more general purpose solution here. + //exe.addIncludeDir("/nix/store/b3zsk4ihlpiimv3vff86bb5bxghgdzb9-gcc-9.2.0/lib/gcc/x86_64-unknown-linux-gnu/9.2.0/../../../../include/c++/9.2.0"); + //exe.addIncludeDir("/nix/store/b3zsk4ihlpiimv3vff86bb5bxghgdzb9-gcc-9.2.0/lib/gcc/x86_64-unknown-linux-gnu/9.2.0/../../../../include/c++/9.2.0/x86_64-unknown-linux-gnu"); + //exe.addIncludeDir("/nix/store/b3zsk4ihlpiimv3vff86bb5bxghgdzb9-gcc-9.2.0/lib/gcc/x86_64-unknown-linux-gnu/9.2.0/../../../../include/c++/9.2.0/backward"); + } } const Context = struct { diff --git a/ci/azure/linux_script b/ci/azure/linux_script index fb4caf18c0..99647ee063 100755 --- a/ci/azure/linux_script +++ b/ci/azure/linux_script @@ -28,22 +28,6 @@ PATH=$PWD/$WASMTIME:$PATH # This will affect the cmake command below. git config core.abbrev 9 -# This patch is a workaround for -# https://bugs.llvm.org/show_bug.cgi?id=44870 / https://github.com/llvm/llvm-project/issues/191 -# It only applies to the apt.llvm.org packages. -patch <<'END_PATCH' ---- CMakeLists.txt -+++ CMakeLists.txt -@@ -369,6 +369,7 @@ target_link_libraries(zig_cpp LINK_PUBLIC - ${CLANG_LIBRARIES} - ${LLD_LIBRARIES} - ${LLVM_LIBRARIES} -+ "-Wl,/usr/lib/llvm-10/lib/LLVMPolly.so" - ) - - add_library(opt_c_util STATIC ${OPTIMIZED_C_SOURCES}) -END_PATCH - export CC=gcc-7 export CXX=g++-7 mkdir build diff --git a/ci/azure/windows_mingw_script b/ci/azure/windows_mingw_script index 7883766427..900baebee3 100644 --- a/ci/azure/windows_mingw_script +++ b/ci/azure/windows_mingw_script @@ -7,6 +7,13 @@ pacman --noconfirm --needed -S git base-devel mingw-w64-x86_64-toolchain mingw-w git config core.abbrev 9 +# Git is wrong for autocrlf being enabled by default on Windows. +# git is mangling files on Windows by default. +# This is the second bug I've tracked down to being caused by autocrlf. +git config core.autocrlf false +# Too late; the files are already mangled. +git checkout . + ZIGBUILDDIR="$(pwd)/build" PREFIX="$ZIGBUILDDIR/dist" CMAKEFLAGS="-DCMAKE_COLOR_MAKEFILE=OFF -DCMAKE_INSTALL_PREFIX=$PREFIX -DZIG_STATIC=ON" diff --git a/ci/srht/freebsd_script b/ci/srht/freebsd_script index 31aea6c3dc..3d9eb73735 100755 --- a/ci/srht/freebsd_script +++ b/ci/srht/freebsd_script @@ -21,6 +21,11 @@ cd $ZIGDIR # This will affect the cmake command below. git config core.abbrev 9 +# SourceHut reports that it is a terminal that supports escape codes, but it +# is a filthy liar. Here we tell Zig to not try to send any terminal escape +# codes to show progress. +export TERM=dumb + mkdir build cd build cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=$PREFIX "-DCMAKE_INSTALL_PREFIX=$(pwd)/release" -DZIG_STATIC=ON diff --git a/doc/docgen.zig b/doc/docgen.zig index af4d2530d0..50523d0948 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -4,7 +4,7 @@ const io = std.io; const fs = std.fs; const process = std.process; const ChildProcess = std.ChildProcess; -const warn = std.debug.warn; +const print = std.debug.print; const mem = std.mem; const testing = std.testing; @@ -215,23 +215,23 @@ const Tokenizer = struct { fn parseError(tokenizer: *Tokenizer, token: Token, comptime fmt: []const u8, args: anytype) anyerror { const loc = tokenizer.getTokenLocation(token); const args_prefix = .{ tokenizer.source_file_name, loc.line + 1, loc.column + 1 }; - warn("{}:{}:{}: error: " ++ fmt ++ "\n", args_prefix ++ args); + print("{}:{}:{}: error: " ++ fmt ++ "\n", args_prefix ++ args); if (loc.line_start <= loc.line_end) { - warn("{}\n", .{tokenizer.buffer[loc.line_start..loc.line_end]}); + print("{}\n", .{tokenizer.buffer[loc.line_start..loc.line_end]}); { var i: usize = 0; while (i < loc.column) : (i += 1) { - warn(" ", .{}); + print(" ", .{}); } } { const caret_count = token.end - token.start; var i: usize = 0; while (i < caret_count) : (i += 1) { - warn("~", .{}); + print("~", .{}); } } - warn("\n", .{}); + print("\n", .{}); } return error.ParseError; } @@ -274,6 +274,7 @@ const Code = struct { link_objects: []const []const u8, target_str: ?[]const u8, link_libc: bool, + disable_cache: bool, const Id = union(enum) { Test, @@ -522,6 +523,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { defer link_objects.deinit(); var target_str: ?[]const u8 = null; var link_libc = false; + var disable_cache = false; const source_token = while (true) { const content_tok = try eatToken(tokenizer, Token.Id.Content); @@ -532,6 +534,8 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { mode = .ReleaseFast; } else if (mem.eql(u8, end_tag_name, "code_release_safe")) { mode = .ReleaseSafe; + } else if (mem.eql(u8, end_tag_name, "code_disable_cache")) { + disable_cache = true; } else if (mem.eql(u8, end_tag_name, "code_link_object")) { _ = try eatToken(tokenizer, Token.Id.Separator); const obj_tok = try eatToken(tokenizer, Token.Id.TagContent); @@ -572,6 +576,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { .link_objects = link_objects.toOwnedSlice(), .target_str = target_str, .link_libc = link_libc, + .disable_cache = disable_cache, }, }); tokenizer.code_node_count += 1; @@ -1032,7 +1037,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any }, .Code => |code| { code_progress_index += 1; - warn("docgen example code {}/{}...", .{ code_progress_index, tokenizer.code_node_count }); + print("docgen example code {}/{}...", .{ code_progress_index, tokenizer.code_node_count }); const raw_source = tokenizer.buffer[code.source_token.start..code.source_token.end]; const trimmed_raw_source = mem.trim(u8, raw_source, " \n"); @@ -1055,30 +1060,17 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any var build_args = std.ArrayList([]const u8).init(allocator); defer build_args.deinit(); try build_args.appendSlice(&[_][]const u8{ - zig_exe, - "build-exe", - tmp_source_file_name, - "--name", - code.name, - "--color", - "on", - "--cache", - "on", + zig_exe, "build-exe", + "--name", code.name, + "--color", "on", + "--enable-cache", tmp_source_file_name, }); try out.print("
$ zig build-exe {}.zig", .{code.name});
switch (code.mode) {
.Debug => {},
- .ReleaseSafe => {
- try build_args.append("--release-safe");
- try out.print(" --release-safe", .{});
- },
- .ReleaseFast => {
- try build_args.append("--release-fast");
- try out.print(" --release-fast", .{});
- },
- .ReleaseSmall => {
- try build_args.append("--release-small");
- try out.print(" --release-small", .{});
+ else => {
+ try build_args.appendSlice(&[_][]const u8{ "-O", @tagName(code.mode) });
+ try out.print(" -O {s}", .{@tagName(code.mode)});
},
}
for (code.link_objects) |link_object| {
@@ -1087,9 +1079,8 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
allocator,
&[_][]const u8{ tmp_dir_name, name_with_ext },
);
- try build_args.append("--object");
try build_args.append(full_path_object);
- try out.print(" --object {}", .{name_with_ext});
+ try out.print(" {s}", .{name_with_ext});
}
if (code.link_libc) {
try build_args.append("-lc");
@@ -1114,20 +1105,14 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
switch (result.term) {
.Exited => |exit_code| {
if (exit_code == 0) {
- warn("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
- for (build_args.items) |arg|
- warn("{} ", .{arg})
- else
- warn("\n", .{});
+ print("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
+ dumpArgs(build_args.items);
return parseError(tokenizer, code.source_token, "example incorrectly compiled", .{});
}
},
else => {
- warn("{}\nThe following command crashed:\n", .{result.stderr});
- for (build_args.items) |arg|
- warn("{} ", .{arg})
- else
- warn("\n", .{});
+ print("{}\nThe following command crashed:\n", .{result.stderr});
+ dumpArgs(build_args.items);
return parseError(tokenizer, code.source_token, "example compile crashed", .{});
},
}
@@ -1174,11 +1159,8 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
switch (result.term) {
.Exited => |exit_code| {
if (exit_code == 0) {
- warn("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
- for (run_args) |arg|
- warn("{} ", .{arg})
- else
- warn("\n", .{});
+ print("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
+ dumpArgs(run_args);
return parseError(tokenizer, code.source_token, "example incorrectly compiled", .{});
}
},
@@ -1206,27 +1188,13 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
var test_args = std.ArrayList([]const u8).init(allocator);
defer test_args.deinit();
- try test_args.appendSlice(&[_][]const u8{
- zig_exe,
- "test",
- tmp_source_file_name,
- "--cache",
- "on",
- });
+ try test_args.appendSlice(&[_][]const u8{ zig_exe, "test", tmp_source_file_name });
try out.print("$ zig test {}.zig", .{code.name});
switch (code.mode) {
.Debug => {},
- .ReleaseSafe => {
- try test_args.append("--release-safe");
- try out.print(" --release-safe", .{});
- },
- .ReleaseFast => {
- try test_args.append("--release-fast");
- try out.print(" --release-fast", .{});
- },
- .ReleaseSmall => {
- try test_args.append("--release-small");
- try out.print(" --release-small", .{});
+ else => {
+ try test_args.appendSlice(&[_][]const u8{ "-O", @tagName(code.mode) });
+ try out.print(" -O {s}", .{@tagName(code.mode)});
},
}
if (code.link_libc) {
@@ -1252,23 +1220,13 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
"--color",
"on",
tmp_source_file_name,
- "--output-dir",
- tmp_dir_name,
});
try out.print("$ zig test {}.zig", .{code.name});
switch (code.mode) {
.Debug => {},
- .ReleaseSafe => {
- try test_args.append("--release-safe");
- try out.print(" --release-safe", .{});
- },
- .ReleaseFast => {
- try test_args.append("--release-fast");
- try out.print(" --release-fast", .{});
- },
- .ReleaseSmall => {
- try test_args.append("--release-small");
- try out.print(" --release-small", .{});
+ else => {
+ try test_args.appendSlice(&[_][]const u8{ "-O", @tagName(code.mode) });
+ try out.print(" -O {s}", .{@tagName(code.mode)});
},
}
const result = try ChildProcess.exec(.{
@@ -1280,25 +1238,19 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
switch (result.term) {
.Exited => |exit_code| {
if (exit_code == 0) {
- warn("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
- for (test_args.items) |arg|
- warn("{} ", .{arg})
- else
- warn("\n", .{});
+ print("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
+ dumpArgs(test_args.items);
return parseError(tokenizer, code.source_token, "example incorrectly compiled", .{});
}
},
else => {
- warn("{}\nThe following command crashed:\n", .{result.stderr});
- for (test_args.items) |arg|
- warn("{} ", .{arg})
- else
- warn("\n", .{});
+ print("{}\nThe following command crashed:\n", .{result.stderr});
+ dumpArgs(test_args.items);
return parseError(tokenizer, code.source_token, "example compile crashed", .{});
},
}
if (mem.indexOf(u8, result.stderr, error_match) == null) {
- warn("{}\nExpected to find '{}' in stderr", .{ result.stderr, error_match });
+ print("{}\nExpected to find '{}' in stderr", .{ result.stderr, error_match });
return parseError(tokenizer, code.source_token, "example did not have expected compile error", .{});
}
const escaped_stderr = try escapeHtml(allocator, result.stderr);
@@ -1314,23 +1266,21 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
zig_exe,
"test",
tmp_source_file_name,
- "--output-dir",
- tmp_dir_name,
});
var mode_arg: []const u8 = "";
switch (code.mode) {
.Debug => {},
.ReleaseSafe => {
- try test_args.append("--release-safe");
- mode_arg = " --release-safe";
+ try test_args.append("-OReleaseSafe");
+ mode_arg = "-OReleaseSafe";
},
.ReleaseFast => {
- try test_args.append("--release-fast");
- mode_arg = " --release-fast";
+ try test_args.append("-OReleaseFast");
+ mode_arg = "-OReleaseFast";
},
.ReleaseSmall => {
- try test_args.append("--release-small");
- mode_arg = " --release-small";
+ try test_args.append("-OReleaseSmall");
+ mode_arg = "-OReleaseSmall";
},
}
@@ -1343,25 +1293,19 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
switch (result.term) {
.Exited => |exit_code| {
if (exit_code == 0) {
- warn("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
- for (test_args.items) |arg|
- warn("{} ", .{arg})
- else
- warn("\n", .{});
+ print("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
+ dumpArgs(test_args.items);
return parseError(tokenizer, code.source_token, "example test incorrectly succeeded", .{});
}
},
else => {
- warn("{}\nThe following command crashed:\n", .{result.stderr});
- for (test_args.items) |arg|
- warn("{} ", .{arg})
- else
- warn("\n", .{});
+ print("{}\nThe following command crashed:\n", .{result.stderr});
+ dumpArgs(test_args.items);
return parseError(tokenizer, code.source_token, "example compile crashed", .{});
},
}
if (mem.indexOf(u8, result.stderr, error_match) == null) {
- warn("{}\nExpected to find '{}' in stderr", .{ result.stderr, error_match });
+ print("{}\nExpected to find '{}' in stderr", .{ result.stderr, error_match });
return parseError(tokenizer, code.source_token, "example did not have expected runtime safety error message", .{});
}
const escaped_stderr = try escapeHtml(allocator, result.stderr);
@@ -1395,32 +1339,20 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
"on",
"--name",
code.name,
- "--output-dir",
- tmp_dir_name,
+ try std.fmt.allocPrint(allocator, "-femit-bin={s}{c}{s}", .{
+ tmp_dir_name, fs.path.sep, name_plus_obj_ext,
+ }),
});
-
if (!code.is_inline) {
try out.print("$ zig build-obj {}.zig", .{code.name});
}
switch (code.mode) {
.Debug => {},
- .ReleaseSafe => {
- try build_args.append("--release-safe");
+ else => {
+ try build_args.appendSlice(&[_][]const u8{ "-O", @tagName(code.mode) });
if (!code.is_inline) {
- try out.print(" --release-safe", .{});
- }
- },
- .ReleaseFast => {
- try build_args.append("--release-fast");
- if (!code.is_inline) {
- try out.print(" --release-fast", .{});
- }
- },
- .ReleaseSmall => {
- try build_args.append("--release-small");
- if (!code.is_inline) {
- try out.print(" --release-small", .{});
+ try out.print(" -O {s}", .{@tagName(code.mode)});
}
},
}
@@ -1440,25 +1372,19 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
switch (result.term) {
.Exited => |exit_code| {
if (exit_code == 0) {
- warn("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
- for (build_args.items) |arg|
- warn("{} ", .{arg})
- else
- warn("\n", .{});
+ print("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
+ dumpArgs(build_args.items);
return parseError(tokenizer, code.source_token, "example build incorrectly succeeded", .{});
}
},
else => {
- warn("{}\nThe following command crashed:\n", .{result.stderr});
- for (build_args.items) |arg|
- warn("{} ", .{arg})
- else
- warn("\n", .{});
+ print("{}\nThe following command crashed:\n", .{result.stderr});
+ dumpArgs(build_args.items);
return parseError(tokenizer, code.source_token, "example compile crashed", .{});
},
}
if (mem.indexOf(u8, result.stderr, error_match) == null) {
- warn("{}\nExpected to find '{}' in stderr", .{ result.stderr, error_match });
+ print("{}\nExpected to find '{}' in stderr", .{ result.stderr, error_match });
return parseError(tokenizer, code.source_token, "example did not have expected compile error message", .{});
}
const escaped_stderr = try escapeHtml(allocator, result.stderr);
@@ -1472,6 +1398,12 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
}
},
Code.Id.Lib => {
+ const bin_basename = try std.zig.binNameAlloc(allocator, .{
+ .root_name = code.name,
+ .target = std.Target.current,
+ .output_mode = .Lib,
+ });
+
var test_args = std.ArrayList([]const u8).init(allocator);
defer test_args.deinit();
@@ -1479,23 +1411,16 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
zig_exe,
"build-lib",
tmp_source_file_name,
- "--output-dir",
- tmp_dir_name,
+ try std.fmt.allocPrint(allocator, "-femit-bin={s}{s}{s}", .{
+ tmp_dir_name, fs.path.sep_str, bin_basename,
+ }),
});
try out.print("$ zig build-lib {}.zig", .{code.name});
switch (code.mode) {
.Debug => {},
- .ReleaseSafe => {
- try test_args.append("--release-safe");
- try out.print(" --release-safe", .{});
- },
- .ReleaseFast => {
- try test_args.append("--release-fast");
- try out.print(" --release-fast", .{});
- },
- .ReleaseSmall => {
- try test_args.append("--release-small");
- try out.print(" --release-small", .{});
+ else => {
+ try test_args.appendSlice(&[_][]const u8{ "-O", @tagName(code.mode) });
+ try out.print(" -O {s}", .{@tagName(code.mode)});
},
}
if (code.target_str) |triple| {
@@ -1508,7 +1433,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
try out.print("\n{}{}
\n", .{ escaped_stderr, escaped_stdout });
},
}
- warn("OK\n", .{});
+ print("OK\n", .{});
},
}
}
@@ -1524,20 +1449,14 @@ fn exec(allocator: *mem.Allocator, env_map: *std.BufMap, args: []const []const u
switch (result.term) {
.Exited => |exit_code| {
if (exit_code != 0) {
- warn("{}\nThe following command exited with code {}:\n", .{ result.stderr, exit_code });
- for (args) |arg|
- warn("{} ", .{arg})
- else
- warn("\n", .{});
+ print("{}\nThe following command exited with code {}:\n", .{ result.stderr, exit_code });
+ dumpArgs(args);
return error.ChildExitError;
}
},
else => {
- warn("{}\nThe following command crashed:\n", .{result.stderr});
- for (args) |arg|
- warn("{} ", .{arg})
- else
- warn("\n", .{});
+ print("{}\nThe following command crashed:\n", .{result.stderr});
+ dumpArgs(args);
return error.ChildCrashed;
},
}
@@ -1545,9 +1464,13 @@ fn exec(allocator: *mem.Allocator, env_map: *std.BufMap, args: []const []const u
}
fn getBuiltinCode(allocator: *mem.Allocator, env_map: *std.BufMap, zig_exe: []const u8) ![]const u8 {
- const result = try exec(allocator, env_map, &[_][]const u8{
- zig_exe,
- "builtin",
- });
+ const result = try exec(allocator, env_map, &[_][]const u8{ zig_exe, "build-obj", "--show-builtin" });
return result.stdout;
}
+
+fn dumpArgs(args: []const []const u8) void {
+ for (args) |arg|
+ print("{} ", .{arg})
+ else
+ print("\n", .{});
+}
diff --git a/doc/langref.html.in b/doc/langref.html.in
index dce23a43e4..22faf7fd8f 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -1078,6 +1078,7 @@ const nan = std.math.nan(f128);
but you can switch to {#syntax#}Optimized{#endsyntax#} mode on a per-block basis:
{#code_begin|obj|foo#}
{#code_release_fast#}
+ {#code_disable_cache#}
const std = @import("std");
const builtin = std.builtin;
const big = @as(f64, 1 << 40);
@@ -1840,7 +1841,7 @@ const Point = struct {
y: i32,
};
-test "compile-time array initalization" {
+test "compile-time array initialization" {
assert(fancy_array[4].x == 4);
assert(fancy_array[4].y == 8);
}
@@ -8467,30 +8468,23 @@ test "integer truncation" {
{#syntax#}@TypeOf(null){#endsyntax#}
{#link|Arrays#}
{#link|Optionals#}
+ {#link|Error Set Type#}
{#link|Error Union Type#}
{#link|Vectors#}
{#link|Opaque Types#}
- AnyFrame
-
-
- For these types it is a
- TODO in the compiler to implement:
-
-
- - ErrorSet
- - Enum
- - FnFrame
- - EnumLiteral
-
-
- For these types, {#syntax#}@Type{#endsyntax#} is not available.
- There is an open proposal to allow unions and structs.
-
-
+ - {#link|@Frame#}
+ - {#syntax#}anyframe{#endsyntax#}
+ - {#link|struct#}
+ - {#link|enum#}
+ - {#link|Enum Literals#}
- {#link|union#}
+
+
+ For these types, {#syntax#}@Type{#endsyntax#} is not available:
+
+
- {#link|Functions#}
- BoundFn
- - {#link|struct#}
{#header_close#}
{#header_open|@typeInfo#}
@@ -9888,12 +9882,13 @@ The result is 3
const std = @import("std");
pub fn main() !void {
- // TODO a better default allocator that isn't as wasteful!
- const args = try std.process.argsAlloc(std.heap.page_allocator);
- defer std.process.argsFree(std.heap.page_allocator, args);
+ var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
+ const gpa = &general_purpose_allocator.allocator;
+ const args = try std.process.argsAlloc(gpa);
+ defer std.process.argsFree(gpa, args);
for (args) |arg, i| {
- std.debug.print("{}: {}\n", .{i, arg});
+ std.debug.print("{}: {}\n", .{ i, arg });
}
}
{#code_end#}
@@ -11392,8 +11387,9 @@ keyword <- KEYWORD_align / KEYWORD_and / KEYWORD_anyframe / KEYWORD_anytype
Incremental improvements.
Avoid local maximums.
Reduce the amount one must remember.
- Minimize energy spent on coding style.
- Resource deallocation must succeed.
+ Focus on code rather than style.
+ Resource allocation may fail; resource deallocation must succeed.
+ Memory is a resource.
Together we serve the users.
{#header_close#}
diff --git a/lib/std/array_hash_map.zig b/lib/std/array_hash_map.zig
index f8c3623ef2..649c1e1055 100644
--- a/lib/std/array_hash_map.zig
+++ b/lib/std/array_hash_map.zig
@@ -112,12 +112,10 @@ pub fn ArrayHashMap(
return self.unmanaged.clearAndFree(self.allocator);
}
- /// Deprecated. Use `items().len`.
pub fn count(self: Self) usize {
- return self.items().len;
+ return self.unmanaged.count();
}
- /// Deprecated. Iterate using `items`.
pub fn iterator(self: *const Self) Iterator {
return Iterator{
.hm = self,
@@ -332,6 +330,10 @@ pub fn ArrayHashMapUnmanaged(
}
}
+ pub fn count(self: Self) usize {
+ return self.entries.items.len;
+ }
+
/// If key exists this function cannot fail.
/// If there is an existing item with `key`, then the result
/// `Entry` pointer points to it, and found_existing is true.
diff --git a/lib/std/bloom_filter.zig b/lib/std/bloom_filter.zig
deleted file mode 100644
index 0e251f548f..0000000000
--- a/lib/std/bloom_filter.zig
+++ /dev/null
@@ -1,265 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 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 builtin = @import("builtin");
-const std = @import("std.zig");
-const math = std.math;
-const debug = std.debug;
-const assert = std.debug.assert;
-const testing = std.testing;
-
-/// There is a trade off of how quickly to fill a bloom filter;
-/// the number of items is:
-/// n_items / K * ln(2)
-/// the rate of false positives is:
-/// (1-e^(-K*N/n_items))^K
-/// where N is the number of items
-pub fn BloomFilter(
- /// Size of bloom filter in cells, must be a power of two.
- comptime n_items: usize,
- /// Number of cells to set per item
- comptime K: usize,
- /// Cell type, should be:
- /// - `bool` for a standard bloom filter
- /// - an unsigned integer type for a counting bloom filter
- comptime Cell: type,
- /// endianess of the Cell
- comptime endian: builtin.Endian,
- /// Hash function to use
- comptime hash: fn (out: []u8, Ki: usize, in: []const u8) void,
-) type {
- assert(n_items > 0);
- assert(math.isPowerOfTwo(n_items));
- assert(K > 0);
- const cellEmpty = if (Cell == bool) false else @as(Cell, 0);
- const cellMax = if (Cell == bool) true else math.maxInt(Cell);
- const n_bytes = (n_items * comptime std.meta.bitCount(Cell)) / 8;
- assert(n_bytes > 0);
- const Io = std.packed_int_array.PackedIntIo(Cell, endian);
-
- return struct {
- const Self = @This();
- pub const items = n_items;
- pub const Index = math.IntFittingRange(0, n_items - 1);
-
- data: [n_bytes]u8 = [_]u8{0} ** n_bytes,
-
- pub fn reset(self: *Self) void {
- std.mem.set(u8, self.data[0..], 0);
- }
-
- pub fn @"union"(x: Self, y: Self) Self {
- var r = Self{ .data = undefined };
- inline for (x.data) |v, i| {
- r.data[i] = v | y.data[i];
- }
- return r;
- }
-
- pub fn intersection(x: Self, y: Self) Self {
- var r = Self{ .data = undefined };
- inline for (x.data) |v, i| {
- r.data[i] = v & y.data[i];
- }
- return r;
- }
-
- pub fn getCell(self: Self, cell: Index) Cell {
- return Io.get(&self.data, cell, 0);
- }
-
- pub fn incrementCell(self: *Self, cell: Index) void {
- if (Cell == bool or Cell == u1) {
- // skip the 'get' operation
- Io.set(&self.data, cell, 0, cellMax);
- } else {
- const old = Io.get(&self.data, cell, 0);
- if (old != cellMax) {
- Io.set(&self.data, cell, 0, old + 1);
- }
- }
- }
-
- pub fn clearCell(self: *Self, cell: Index) void {
- Io.set(&self.data, cell, 0, cellEmpty);
- }
-
- pub fn add(self: *Self, item: []const u8) void {
- comptime var i = 0;
- inline while (i < K) : (i += 1) {
- var K_th_bit: packed struct {
- x: Index,
- } = undefined;
- hash(std.mem.asBytes(&K_th_bit), i, item);
- incrementCell(self, K_th_bit.x);
- }
- }
-
- pub fn contains(self: Self, item: []const u8) bool {
- comptime var i = 0;
- inline while (i < K) : (i += 1) {
- var K_th_bit: packed struct {
- x: Index,
- } = undefined;
- hash(std.mem.asBytes(&K_th_bit), i, item);
- if (getCell(self, K_th_bit.x) == cellEmpty)
- return false;
- }
- return true;
- }
-
- pub fn resize(self: Self, comptime newsize: usize) BloomFilter(newsize, K, Cell, endian, hash) {
- var r: BloomFilter(newsize, K, Cell, endian, hash) = undefined;
- if (newsize < n_items) {
- std.mem.copy(u8, r.data[0..], self.data[0..r.data.len]);
- var copied: usize = r.data.len;
- while (copied < self.data.len) : (copied += r.data.len) {
- for (self.data[copied .. copied + r.data.len]) |s, i| {
- r.data[i] |= s;
- }
- }
- } else if (newsize == n_items) {
- r = self;
- } else if (newsize > n_items) {
- var copied: usize = 0;
- while (copied < r.data.len) : (copied += self.data.len) {
- std.mem.copy(u8, r.data[copied .. copied + self.data.len], &self.data);
- }
- }
- return r;
- }
-
- /// Returns number of non-zero cells
- pub fn popCount(self: Self) Index {
- var n: Index = 0;
- if (Cell == bool or Cell == u1) {
- for (self.data) |b, i| {
- n += @popCount(u8, b);
- }
- } else {
- var i: usize = 0;
- while (i < n_items) : (i += 1) {
- const cell = self.getCell(@intCast(Index, i));
- n += if (if (Cell == bool) cell else cell > 0) @as(Index, 1) else @as(Index, 0);
- }
- }
- return n;
- }
-
- pub fn estimateItems(self: Self) f64 {
- const m = comptime @intToFloat(f64, n_items);
- const k = comptime @intToFloat(f64, K);
- const X = @intToFloat(f64, self.popCount());
- return (comptime (-m / k)) * math.log1p(X * comptime (-1 / m));
- }
- };
-}
-
-fn hashFunc(out: []u8, Ki: usize, in: []const u8) void {
- var st = std.crypto.hash.Gimli.init(.{});
- st.update(std.mem.asBytes(&Ki));
- st.update(in);
- st.final(out);
-}
-
-test "std.BloomFilter" {
- // https://github.com/ziglang/zig/issues/5127
- if (std.Target.current.cpu.arch == .mips) return error.SkipZigTest;
-
- inline for ([_]type{ bool, u1, u2, u3, u4 }) |Cell| {
- const emptyCell = if (Cell == bool) false else @as(Cell, 0);
- const BF = BloomFilter(128 * 8, 8, Cell, builtin.endian, hashFunc);
- var bf = BF{};
- var i: usize = undefined;
- // confirm that it is initialised to the empty filter
- i = 0;
- while (i < BF.items) : (i += 1) {
- testing.expectEqual(emptyCell, bf.getCell(@intCast(BF.Index, i)));
- }
- testing.expectEqual(@as(BF.Index, 0), bf.popCount());
- testing.expectEqual(@as(f64, 0), bf.estimateItems());
- // fill in a few items
- bf.incrementCell(42);
- bf.incrementCell(255);
- bf.incrementCell(256);
- bf.incrementCell(257);
- // check that they were set
- testing.expectEqual(true, bf.getCell(42) != emptyCell);
- testing.expectEqual(true, bf.getCell(255) != emptyCell);
- testing.expectEqual(true, bf.getCell(256) != emptyCell);
- testing.expectEqual(true, bf.getCell(257) != emptyCell);
- // clear just one of them; make sure the rest are still set
- bf.clearCell(256);
- testing.expectEqual(true, bf.getCell(42) != emptyCell);
- testing.expectEqual(true, bf.getCell(255) != emptyCell);
- testing.expectEqual(false, bf.getCell(256) != emptyCell);
- testing.expectEqual(true, bf.getCell(257) != emptyCell);
- // reset any of the ones we've set and confirm we're back to the empty filter
- bf.clearCell(42);
- bf.clearCell(255);
- bf.clearCell(257);
- i = 0;
- while (i < BF.items) : (i += 1) {
- testing.expectEqual(emptyCell, bf.getCell(@intCast(BF.Index, i)));
- }
- testing.expectEqual(@as(BF.Index, 0), bf.popCount());
- testing.expectEqual(@as(f64, 0), bf.estimateItems());
-
- // Lets add a string
- bf.add("foo");
- testing.expectEqual(true, bf.contains("foo"));
- {
- // try adding same string again. make sure popcount is the same
- const old_popcount = bf.popCount();
- testing.expect(old_popcount > 0);
- bf.add("foo");
- testing.expectEqual(true, bf.contains("foo"));
- testing.expectEqual(old_popcount, bf.popCount());
- }
-
- // Get back to empty filter via .reset
- bf.reset();
- // Double check that .reset worked
- i = 0;
- while (i < BF.items) : (i += 1) {
- testing.expectEqual(emptyCell, bf.getCell(@intCast(BF.Index, i)));
- }
- testing.expectEqual(@as(BF.Index, 0), bf.popCount());
- testing.expectEqual(@as(f64, 0), bf.estimateItems());
-
- comptime var teststrings = [_][]const u8{
- "foo",
- "bar",
- "a longer string",
- "some more",
- "the quick brown fox",
- "unique string",
- };
- inline for (teststrings) |str| {
- bf.add(str);
- }
- inline for (teststrings) |str| {
- testing.expectEqual(true, bf.contains(str));
- }
-
- { // estimate should be close for low packing
- const est = bf.estimateItems();
- testing.expect(est > @intToFloat(f64, teststrings.len) - 1);
- testing.expect(est < @intToFloat(f64, teststrings.len) + 1);
- }
-
- const larger_bf = bf.resize(4096);
- inline for (teststrings) |str| {
- testing.expectEqual(true, larger_bf.contains(str));
- }
- testing.expectEqual(@as(u12, bf.popCount()) * (4096 / 1024), larger_bf.popCount());
-
- const smaller_bf = bf.resize(64);
- inline for (teststrings) |str| {
- testing.expectEqual(true, smaller_bf.contains(str));
- }
- testing.expect(bf.popCount() <= @as(u10, smaller_bf.popCount()) * (1024 / 64));
- }
-}
diff --git a/lib/std/build.zig b/lib/std/build.zig
index 69f44bad32..7e3c75bc78 100644
--- a/lib/std/build.zig
+++ b/lib/std/build.zig
@@ -1165,6 +1165,11 @@ pub const FileSource = union(enum) {
}
};
+const BuildOptionArtifactArg = struct {
+ name: []const u8,
+ artifact: *LibExeObjStep,
+};
+
pub const LibExeObjStep = struct {
step: Step,
builder: *Builder,
@@ -1210,6 +1215,7 @@ pub const LibExeObjStep = struct {
out_pdb_filename: []const u8,
packages: ArrayList(Pkg),
build_options_contents: std.ArrayList(u8),
+ build_options_artifact_args: std.ArrayList(BuildOptionArtifactArg),
system_linker_hack: bool = false,
object_src: []const u8,
@@ -1355,6 +1361,7 @@ pub const LibExeObjStep = struct {
.framework_dirs = ArrayList([]const u8).init(builder.allocator),
.object_src = undefined,
.build_options_contents = std.ArrayList(u8).init(builder.allocator),
+ .build_options_artifact_args = std.ArrayList(BuildOptionArtifactArg).init(builder.allocator),
.c_std = Builder.CStd.C99,
.override_lib_dir = null,
.main_pkg_path = null,
@@ -1377,6 +1384,7 @@ pub const LibExeObjStep = struct {
}
fn computeOutFileNames(self: *LibExeObjStep) void {
+ // TODO make this call std.zig.binNameAlloc
switch (self.kind) {
.Obj => {
self.out_filename = self.builder.fmt("{}{}", .{ self.name, self.target.oFileExt() });
@@ -1692,8 +1700,6 @@ pub const LibExeObjStep = struct {
self.main_pkg_path = dir_path;
}
- pub const setDisableGenH = @compileError("deprecated; set the emit_h field directly");
-
pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?[]const u8) void {
self.libc_file = libc_file;
}
@@ -1812,6 +1818,13 @@ pub const LibExeObjStep = struct {
out.print("pub const {} = {};\n", .{ name, value }) catch unreachable;
}
+ /// The value is the path in the cache dir.
+ /// Adds a dependency automatically.
+ pub fn addBuildOptionArtifact(self: *LibExeObjStep, name: []const u8, artifact: *LibExeObjStep) void {
+ self.build_options_artifact_args.append(.{ .name = name, .artifact = artifact }) catch unreachable;
+ self.step.dependOn(&artifact.step);
+ }
+
pub fn addSystemIncludeDir(self: *LibExeObjStep, path: []const u8) void {
self.include_dirs.append(IncludeDir{ .RawPathSystem = self.builder.dupe(path) }) catch unreachable;
}
@@ -1947,10 +1960,10 @@ pub const LibExeObjStep = struct {
if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));
+ var prev_has_extra_flags = false;
for (self.link_objects.span()) |link_object| {
switch (link_object) {
.StaticPath => |static_path| {
- try zig_args.append("--object");
try zig_args.append(builder.pathFromRoot(static_path));
},
@@ -1958,12 +1971,10 @@ pub const LibExeObjStep = struct {
.Exe => unreachable,
.Test => unreachable,
.Obj => {
- try zig_args.append("--object");
try zig_args.append(other.getOutputPath());
},
.Lib => {
if (!other.is_dynamic or self.target.isWindows()) {
- try zig_args.append("--object");
try zig_args.append(other.getOutputLibPath());
} else {
const full_path_lib = other.getOutputPath();
@@ -1982,20 +1993,41 @@ pub const LibExeObjStep = struct {
try zig_args.append(name);
},
.AssemblyFile => |asm_file| {
- try zig_args.append("--c-source");
+ if (prev_has_extra_flags) {
+ try zig_args.append("-extra-cflags");
+ try zig_args.append("--");
+ prev_has_extra_flags = false;
+ }
try zig_args.append(asm_file.getPath(builder));
},
.CSourceFile => |c_source_file| {
- try zig_args.append("--c-source");
- for (c_source_file.args) |arg| {
- try zig_args.append(arg);
+ if (c_source_file.args.len == 0) {
+ if (prev_has_extra_flags) {
+ try zig_args.append("-cflags");
+ try zig_args.append("--");
+ prev_has_extra_flags = false;
+ }
+ } else {
+ try zig_args.append("-cflags");
+ for (c_source_file.args) |arg| {
+ try zig_args.append(arg);
+ }
+ try zig_args.append("--");
}
try zig_args.append(c_source_file.source.getPath(builder));
},
}
}
- if (self.build_options_contents.items.len > 0) {
+ if (self.build_options_contents.items.len > 0 or self.build_options_artifact_args.items.len > 0) {
+ // Render build artifact options at the last minute, now that the path is known.
+ for (self.build_options_artifact_args.items) |item| {
+ const out = self.build_options_contents.writer();
+ out.print("pub const {}: []const u8 = ", .{item.name}) catch unreachable;
+ std.zig.renderStringLiteral(item.artifact.getOutputPath(), out) catch unreachable;
+ out.writeAll(";\n") catch unreachable;
+ }
+
const build_options_file = try fs.path.join(
builder.allocator,
&[_][]const u8{ builder.cache_root, builder.fmt("{}_build_options.zig", .{self.name}) },
@@ -2056,10 +2088,8 @@ pub const LibExeObjStep = struct {
}
switch (self.build_mode) {
- .Debug => {},
- .ReleaseSafe => zig_args.append("--release-safe") catch unreachable,
- .ReleaseFast => zig_args.append("--release-fast") catch unreachable,
- .ReleaseSmall => zig_args.append("--release-small") catch unreachable,
+ .Debug => {}, // Skip since it's the default.
+ else => zig_args.append(builder.fmt("-O{s}", .{@tagName(self.build_mode)})) catch unreachable,
}
try zig_args.append("--cache-dir");
@@ -2070,14 +2100,8 @@ pub const LibExeObjStep = struct {
if (self.kind == Kind.Lib and self.is_dynamic) {
if (self.version) |version| {
- zig_args.append("--ver-major") catch unreachable;
- zig_args.append(builder.fmt("{}", .{version.major})) catch unreachable;
-
- zig_args.append("--ver-minor") catch unreachable;
- zig_args.append(builder.fmt("{}", .{version.minor})) catch unreachable;
-
- zig_args.append("--ver-patch") catch unreachable;
- zig_args.append(builder.fmt("{}", .{version.patch})) catch unreachable;
+ zig_args.append("--version") catch unreachable;
+ zig_args.append(builder.fmt("{}", .{version})) catch unreachable;
}
}
if (self.is_dynamic) {
@@ -2294,8 +2318,7 @@ pub const LibExeObjStep = struct {
if (self.kind == Kind.Test) {
try builder.spawnChild(zig_args.span());
} else {
- try zig_args.append("--cache");
- try zig_args.append("on");
+ try zig_args.append("--enable-cache");
const output_dir_nl = try builder.execFromStep(zig_args.span(), &self.step);
const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
diff --git a/lib/std/build/translate_c.zig b/lib/std/build/translate_c.zig
index 8ca2b87209..87e153066d 100644
--- a/lib/std/build/translate_c.zig
+++ b/lib/std/build/translate_c.zig
@@ -72,8 +72,7 @@ pub const TranslateCStep = struct {
try argv_list.append("translate-c");
try argv_list.append("-lc");
- try argv_list.append("--cache");
- try argv_list.append("on");
+ try argv_list.append("--enable-cache");
if (!self.target.isNative()) {
try argv_list.append("-target");
diff --git a/lib/std/cache_hash.zig b/lib/std/cache_hash.zig
deleted file mode 100644
index 5cd8194e21..0000000000
--- a/lib/std/cache_hash.zig
+++ /dev/null
@@ -1,726 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 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 crypto = std.crypto;
-const Hasher = crypto.auth.siphash.SipHash128(1, 3); // provides enough collision resistance for the CacheHash use cases, while being one of our fastest options right now
-const fs = std.fs;
-const base64 = std.base64;
-const ArrayList = std.ArrayList;
-const assert = std.debug.assert;
-const testing = std.testing;
-const mem = std.mem;
-const fmt = std.fmt;
-const Allocator = std.mem.Allocator;
-
-const base64_encoder = fs.base64_encoder;
-const base64_decoder = fs.base64_decoder;
-/// This is 128 bits - Even with 2^54 cache entries, the probably of a collision would be under 10^-6
-const BIN_DIGEST_LEN = 16;
-const BASE64_DIGEST_LEN = base64.Base64Encoder.calcSize(BIN_DIGEST_LEN);
-
-const MANIFEST_FILE_SIZE_MAX = 50 * 1024 * 1024;
-
-pub const File = struct {
- path: ?[]const u8,
- max_file_size: ?usize,
- stat: fs.File.Stat,
- bin_digest: [BIN_DIGEST_LEN]u8,
- contents: ?[]const u8,
-
- pub fn deinit(self: *File, allocator: *Allocator) void {
- if (self.path) |owned_slice| {
- allocator.free(owned_slice);
- self.path = null;
- }
- if (self.contents) |contents| {
- allocator.free(contents);
- self.contents = null;
- }
- self.* = undefined;
- }
-};
-
-/// CacheHash manages project-local `zig-cache` directories.
-/// This is not a general-purpose cache.
-/// It was designed to be fast and simple, not to withstand attacks using specially-crafted input.
-pub const CacheHash = struct {
- allocator: *Allocator,
- hasher_init: Hasher, // initial state, that can be copied
- hasher: Hasher, // current state for incremental hashing
- manifest_dir: fs.Dir,
- manifest_file: ?fs.File,
- manifest_dirty: bool,
- files: ArrayList(File),
- b64_digest: [BASE64_DIGEST_LEN]u8,
-
- /// Be sure to call release after successful initialization.
- pub fn init(allocator: *Allocator, dir: fs.Dir, manifest_dir_path: []const u8) !CacheHash {
- const hasher_init = Hasher.init(&[_]u8{0} ** Hasher.minimum_key_length);
- return CacheHash{
- .allocator = allocator,
- .hasher_init = hasher_init,
- .hasher = hasher_init,
- .manifest_dir = try dir.makeOpenPath(manifest_dir_path, .{}),
- .manifest_file = null,
- .manifest_dirty = false,
- .files = ArrayList(File).init(allocator),
- .b64_digest = undefined,
- };
- }
-
- /// Record a slice of bytes as an dependency of the process being cached
- pub fn addSlice(self: *CacheHash, val: []const u8) void {
- assert(self.manifest_file == null);
-
- self.hasher.update(val);
- self.hasher.update(&[_]u8{0});
- }
-
- /// Convert the input value into bytes and record it as a dependency of the
- /// process being cached
- pub fn add(self: *CacheHash, val: anytype) void {
- assert(self.manifest_file == null);
-
- const valPtr = switch (@typeInfo(@TypeOf(val))) {
- .Int => &val,
- .Pointer => val,
- else => &val,
- };
-
- self.addSlice(mem.asBytes(valPtr));
- }
-
- /// Add a file as a dependency of process being cached. When `CacheHash.hit` is
- /// called, the file's contents will be checked to ensure that it matches
- /// the contents from previous times.
- ///
- /// Max file size will be used to determine the amount of space to the file contents
- /// are allowed to take up in memory. If max_file_size is null, then the contents
- /// will not be loaded into memory.
- ///
- /// Returns the index of the entry in the `CacheHash.files` ArrayList. You can use it
- /// to access the contents of the file after calling `CacheHash.hit()` like so:
- ///
- /// ```
- /// var file_contents = cache_hash.files.items[file_index].contents.?;
- /// ```
- pub fn addFile(self: *CacheHash, file_path: []const u8, max_file_size: ?usize) !usize {
- assert(self.manifest_file == null);
-
- try self.files.ensureCapacity(self.files.items.len + 1);
- const resolved_path = try fs.path.resolve(self.allocator, &[_][]const u8{file_path});
-
- const idx = self.files.items.len;
- self.files.addOneAssumeCapacity().* = .{
- .path = resolved_path,
- .contents = null,
- .max_file_size = max_file_size,
- .stat = undefined,
- .bin_digest = undefined,
- };
-
- self.addSlice(resolved_path);
-
- return idx;
- }
-
- /// Check the cache to see if the input exists in it. If it exists, a base64 encoding
- /// of it's hash will be returned; otherwise, null will be returned.
- ///
- /// This function will also acquire an exclusive lock to the manifest file. This means
- /// that a process holding a CacheHash will block any other process attempting to
- /// acquire the lock.
- ///
- /// The lock on the manifest file is released when `CacheHash.release` is called.
- pub fn hit(self: *CacheHash) !?[BASE64_DIGEST_LEN]u8 {
- assert(self.manifest_file == null);
-
- var bin_digest: [BIN_DIGEST_LEN]u8 = undefined;
- self.hasher.final(&bin_digest);
-
- base64_encoder.encode(self.b64_digest[0..], &bin_digest);
-
- self.hasher = self.hasher_init;
- self.hasher.update(&bin_digest);
-
- const manifest_file_path = try fmt.allocPrint(self.allocator, "{}.txt", .{self.b64_digest});
- defer self.allocator.free(manifest_file_path);
-
- if (self.files.items.len != 0) {
- self.manifest_file = try self.manifest_dir.createFile(manifest_file_path, .{
- .read = true,
- .truncate = false,
- .lock = .Exclusive,
- });
- } else {
- // If there are no file inputs, we check if the manifest file exists instead of
- // comparing the hashes on the files used for the cached item
- self.manifest_file = self.manifest_dir.openFile(manifest_file_path, .{
- .read = true,
- .write = true,
- .lock = .Exclusive,
- }) catch |err| switch (err) {
- error.FileNotFound => {
- self.manifest_dirty = true;
- self.manifest_file = try self.manifest_dir.createFile(manifest_file_path, .{
- .read = true,
- .truncate = false,
- .lock = .Exclusive,
- });
- return null;
- },
- else => |e| return e,
- };
- }
-
- const file_contents = try self.manifest_file.?.inStream().readAllAlloc(self.allocator, MANIFEST_FILE_SIZE_MAX);
- defer self.allocator.free(file_contents);
-
- const input_file_count = self.files.items.len;
- var any_file_changed = false;
- var line_iter = mem.tokenize(file_contents, "\n");
- var idx: usize = 0;
- while (line_iter.next()) |line| {
- defer idx += 1;
-
- const cache_hash_file = if (idx < input_file_count) &self.files.items[idx] else blk: {
- const new = try self.files.addOne();
- new.* = .{
- .path = null,
- .contents = null,
- .max_file_size = null,
- .stat = undefined,
- .bin_digest = undefined,
- };
- break :blk new;
- };
-
- var iter = mem.tokenize(line, " ");
- const size = iter.next() orelse return error.InvalidFormat;
- const inode = iter.next() orelse return error.InvalidFormat;
- const mtime_nsec_str = iter.next() orelse return error.InvalidFormat;
- const digest_str = iter.next() orelse return error.InvalidFormat;
- const file_path = iter.rest();
-
- cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat;
- cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat;
- cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat;
- base64_decoder.decode(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat;
-
- if (file_path.len == 0) {
- return error.InvalidFormat;
- }
- if (cache_hash_file.path) |p| {
- if (!mem.eql(u8, file_path, p)) {
- return error.InvalidFormat;
- }
- }
-
- if (cache_hash_file.path == null) {
- cache_hash_file.path = try self.allocator.dupe(u8, file_path);
- }
-
- const this_file = fs.cwd().openFile(cache_hash_file.path.?, .{ .read = true }) catch {
- return error.CacheUnavailable;
- };
- defer this_file.close();
-
- const actual_stat = try this_file.stat();
- const size_match = actual_stat.size == cache_hash_file.stat.size;
- const mtime_match = actual_stat.mtime == cache_hash_file.stat.mtime;
- const inode_match = actual_stat.inode == cache_hash_file.stat.inode;
-
- if (!size_match or !mtime_match or !inode_match) {
- self.manifest_dirty = true;
-
- cache_hash_file.stat = actual_stat;
-
- if (isProblematicTimestamp(cache_hash_file.stat.mtime)) {
- cache_hash_file.stat.mtime = 0;
- cache_hash_file.stat.inode = 0;
- }
-
- var actual_digest: [BIN_DIGEST_LEN]u8 = undefined;
- try hashFile(this_file, &actual_digest, self.hasher_init);
-
- if (!mem.eql(u8, &cache_hash_file.bin_digest, &actual_digest)) {
- cache_hash_file.bin_digest = actual_digest;
- // keep going until we have the input file digests
- any_file_changed = true;
- }
- }
-
- if (!any_file_changed) {
- self.hasher.update(&cache_hash_file.bin_digest);
- }
- }
-
- if (any_file_changed) {
- // cache miss
- // keep the manifest file open
- // reset the hash
- self.hasher = self.hasher_init;
- self.hasher.update(&bin_digest);
-
- // Remove files not in the initial hash
- for (self.files.items[input_file_count..]) |*file| {
- file.deinit(self.allocator);
- }
- self.files.shrink(input_file_count);
-
- for (self.files.items) |file| {
- self.hasher.update(&file.bin_digest);
- }
- return null;
- }
-
- if (idx < input_file_count) {
- self.manifest_dirty = true;
- while (idx < input_file_count) : (idx += 1) {
- const ch_file = &self.files.items[idx];
- try self.populateFileHash(ch_file);
- }
- return null;
- }
-
- return self.final();
- }
-
- fn populateFileHash(self: *CacheHash, ch_file: *File) !void {
- const file = try fs.cwd().openFile(ch_file.path.?, .{});
- defer file.close();
-
- ch_file.stat = try file.stat();
-
- if (isProblematicTimestamp(ch_file.stat.mtime)) {
- ch_file.stat.mtime = 0;
- ch_file.stat.inode = 0;
- }
-
- if (ch_file.max_file_size) |max_file_size| {
- if (ch_file.stat.size > max_file_size) {
- return error.FileTooBig;
- }
-
- const contents = try self.allocator.alloc(u8, @intCast(usize, ch_file.stat.size));
- errdefer self.allocator.free(contents);
-
- // Hash while reading from disk, to keep the contents in the cpu cache while
- // doing hashing.
- var hasher = self.hasher_init;
- var off: usize = 0;
- while (true) {
- // give me everything you've got, captain
- const bytes_read = try file.read(contents[off..]);
- if (bytes_read == 0) break;
- hasher.update(contents[off..][0..bytes_read]);
- off += bytes_read;
- }
- hasher.final(&ch_file.bin_digest);
-
- ch_file.contents = contents;
- } else {
- try hashFile(file, &ch_file.bin_digest, self.hasher_init);
- }
-
- self.hasher.update(&ch_file.bin_digest);
- }
-
- /// Add a file as a dependency of process being cached, after the initial hash has been
- /// calculated. This is useful for processes that don't know the all the files that
- /// are depended on ahead of time. For example, a source file that can import other files
- /// will need to be recompiled if the imported file is changed.
- pub fn addFilePostFetch(self: *CacheHash, file_path: []const u8, max_file_size: usize) ![]u8 {
- assert(self.manifest_file != null);
-
- const resolved_path = try fs.path.resolve(self.allocator, &[_][]const u8{file_path});
- errdefer self.allocator.free(resolved_path);
-
- const new_ch_file = try self.files.addOne();
- new_ch_file.* = .{
- .path = resolved_path,
- .max_file_size = max_file_size,
- .stat = undefined,
- .bin_digest = undefined,
- .contents = null,
- };
- errdefer self.files.shrink(self.files.items.len - 1);
-
- try self.populateFileHash(new_ch_file);
-
- return new_ch_file.contents.?;
- }
-
- /// Add a file as a dependency of process being cached, after the initial hash has been
- /// calculated. This is useful for processes that don't know the all the files that
- /// are depended on ahead of time. For example, a source file that can import other files
- /// will need to be recompiled if the imported file is changed.
- pub fn addFilePost(self: *CacheHash, file_path: []const u8) !void {
- assert(self.manifest_file != null);
-
- const resolved_path = try fs.path.resolve(self.allocator, &[_][]const u8{file_path});
- errdefer self.allocator.free(resolved_path);
-
- const new_ch_file = try self.files.addOne();
- new_ch_file.* = .{
- .path = resolved_path,
- .max_file_size = null,
- .stat = undefined,
- .bin_digest = undefined,
- .contents = null,
- };
- errdefer self.files.shrink(self.files.items.len - 1);
-
- try self.populateFileHash(new_ch_file);
- }
-
- /// Returns a base64 encoded hash of the inputs.
- pub fn final(self: *CacheHash) [BASE64_DIGEST_LEN]u8 {
- assert(self.manifest_file != null);
-
- // We don't close the manifest file yet, because we want to
- // keep it locked until the API user is done using it.
- // We also don't write out the manifest yet, because until
- // cache_release is called we still might be working on creating
- // the artifacts to cache.
-
- var bin_digest: [BIN_DIGEST_LEN]u8 = undefined;
- self.hasher.final(&bin_digest);
-
- var out_digest: [BASE64_DIGEST_LEN]u8 = undefined;
- base64_encoder.encode(&out_digest, &bin_digest);
-
- return out_digest;
- }
-
- pub fn writeManifest(self: *CacheHash) !void {
- assert(self.manifest_file != null);
-
- var encoded_digest: [BASE64_DIGEST_LEN]u8 = undefined;
- var contents = ArrayList(u8).init(self.allocator);
- var outStream = contents.outStream();
- defer contents.deinit();
-
- for (self.files.items) |file| {
- base64_encoder.encode(encoded_digest[0..], &file.bin_digest);
- try outStream.print("{} {} {} {} {}\n", .{ file.stat.size, file.stat.inode, file.stat.mtime, encoded_digest[0..], file.path });
- }
-
- try self.manifest_file.?.pwriteAll(contents.items, 0);
- self.manifest_dirty = false;
- }
-
- /// Releases the manifest file and frees any memory the CacheHash was using.
- /// `CacheHash.hit` must be called first.
- ///
- /// Will also attempt to write to the manifest file if the manifest is dirty.
- /// Writing to the manifest file can fail, but this function ignores those errors.
- /// To detect failures from writing the manifest, one may explicitly call
- /// `writeManifest` before `release`.
- pub fn release(self: *CacheHash) void {
- if (self.manifest_file) |file| {
- if (self.manifest_dirty) {
- // To handle these errors, API users should call
- // writeManifest before release().
- self.writeManifest() catch {};
- }
-
- file.close();
- }
-
- for (self.files.items) |*file| {
- file.deinit(self.allocator);
- }
- self.files.deinit();
- self.manifest_dir.close();
- }
-};
-
-fn hashFile(file: fs.File, bin_digest: []u8, hasher_init: anytype) !void {
- var buf: [1024]u8 = undefined;
-
- var hasher = hasher_init;
- while (true) {
- const bytes_read = try file.read(&buf);
- if (bytes_read == 0) break;
- hasher.update(buf[0..bytes_read]);
- }
-
- hasher.final(bin_digest);
-}
-
-/// If the wall clock time, rounded to the same precision as the
-/// mtime, is equal to the mtime, then we cannot rely on this mtime
-/// yet. We will instead save an mtime value that indicates the hash
-/// must be unconditionally computed.
-/// This function recognizes the precision of mtime by looking at trailing
-/// zero bits of the seconds and nanoseconds.
-fn isProblematicTimestamp(fs_clock: i128) bool {
- const wall_clock = std.time.nanoTimestamp();
-
- // We have to break the nanoseconds into seconds and remainder nanoseconds
- // to detect precision of seconds, because looking at the zero bits in base
- // 2 would not detect precision of the seconds value.
- const fs_sec = @intCast(i64, @divFloor(fs_clock, std.time.ns_per_s));
- const fs_nsec = @intCast(i64, @mod(fs_clock, std.time.ns_per_s));
- var wall_sec = @intCast(i64, @divFloor(wall_clock, std.time.ns_per_s));
- var wall_nsec = @intCast(i64, @mod(wall_clock, std.time.ns_per_s));
-
- // First make all the least significant zero bits in the fs_clock, also zero bits in the wall clock.
- if (fs_nsec == 0) {
- wall_nsec = 0;
- if (fs_sec == 0) {
- wall_sec = 0;
- } else {
- wall_sec &= @as(i64, -1) << @intCast(u6, @ctz(i64, fs_sec));
- }
- } else {
- wall_nsec &= @as(i64, -1) << @intCast(u6, @ctz(i64, fs_nsec));
- }
- return wall_nsec == fs_nsec and wall_sec == fs_sec;
-}
-
-test "cache file and then recall it" {
- if (std.Target.current.os.tag == .wasi) {
- // https://github.com/ziglang/zig/issues/5437
- return error.SkipZigTest;
- }
- const cwd = fs.cwd();
-
- const temp_file = "test.txt";
- const temp_manifest_dir = "temp_manifest_dir";
-
- const ts = std.time.nanoTimestamp();
- try cwd.writeFile(temp_file, "Hello, world!\n");
-
- while (isProblematicTimestamp(ts)) {
- std.time.sleep(1);
- }
-
- var digest1: [BASE64_DIGEST_LEN]u8 = undefined;
- var digest2: [BASE64_DIGEST_LEN]u8 = undefined;
-
- {
- var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);
- defer ch.release();
-
- ch.add(true);
- ch.add(@as(u16, 1234));
- ch.add("1234");
- _ = try ch.addFile(temp_file, null);
-
- // There should be nothing in the cache
- testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit());
-
- digest1 = ch.final();
- }
- {
- var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);
- defer ch.release();
-
- ch.add(true);
- ch.add(@as(u16, 1234));
- ch.add("1234");
- _ = try ch.addFile(temp_file, null);
-
- // Cache hit! We just "built" the same file
- digest2 = (try ch.hit()).?;
- }
-
- testing.expectEqual(digest1, digest2);
-
- try cwd.deleteTree(temp_manifest_dir);
- try cwd.deleteFile(temp_file);
-}
-
-test "give problematic timestamp" {
- var fs_clock = std.time.nanoTimestamp();
- // to make it problematic, we make it only accurate to the second
- fs_clock = @divTrunc(fs_clock, std.time.ns_per_s);
- fs_clock *= std.time.ns_per_s;
- testing.expect(isProblematicTimestamp(fs_clock));
-}
-
-test "give nonproblematic timestamp" {
- testing.expect(!isProblematicTimestamp(std.time.nanoTimestamp() - std.time.ns_per_s));
-}
-
-test "check that changing a file makes cache fail" {
- if (std.Target.current.os.tag == .wasi) {
- // https://github.com/ziglang/zig/issues/5437
- return error.SkipZigTest;
- }
- const cwd = fs.cwd();
-
- const temp_file = "cache_hash_change_file_test.txt";
- const temp_manifest_dir = "cache_hash_change_file_manifest_dir";
- const original_temp_file_contents = "Hello, world!\n";
- const updated_temp_file_contents = "Hello, world; but updated!\n";
-
- try cwd.deleteTree(temp_manifest_dir);
- try cwd.deleteTree(temp_file);
-
- const ts = std.time.nanoTimestamp();
- try cwd.writeFile(temp_file, original_temp_file_contents);
-
- while (isProblematicTimestamp(ts)) {
- std.time.sleep(1);
- }
-
- var digest1: [BASE64_DIGEST_LEN]u8 = undefined;
- var digest2: [BASE64_DIGEST_LEN]u8 = undefined;
-
- {
- var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);
- defer ch.release();
-
- ch.add("1234");
- const temp_file_idx = try ch.addFile(temp_file, 100);
-
- // There should be nothing in the cache
- testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit());
-
- testing.expect(mem.eql(u8, original_temp_file_contents, ch.files.items[temp_file_idx].contents.?));
-
- digest1 = ch.final();
- }
-
- try cwd.writeFile(temp_file, updated_temp_file_contents);
-
- {
- var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);
- defer ch.release();
-
- ch.add("1234");
- const temp_file_idx = try ch.addFile(temp_file, 100);
-
- // A file that we depend on has been updated, so the cache should not contain an entry for it
- testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit());
-
- // The cache system does not keep the contents of re-hashed input files.
- testing.expect(ch.files.items[temp_file_idx].contents == null);
-
- digest2 = ch.final();
- }
-
- testing.expect(!mem.eql(u8, digest1[0..], digest2[0..]));
-
- try cwd.deleteTree(temp_manifest_dir);
- try cwd.deleteTree(temp_file);
-}
-
-test "no file inputs" {
- if (std.Target.current.os.tag == .wasi) {
- // https://github.com/ziglang/zig/issues/5437
- return error.SkipZigTest;
- }
- const cwd = fs.cwd();
- const temp_manifest_dir = "no_file_inputs_manifest_dir";
- defer cwd.deleteTree(temp_manifest_dir) catch unreachable;
-
- var digest1: [BASE64_DIGEST_LEN]u8 = undefined;
- var digest2: [BASE64_DIGEST_LEN]u8 = undefined;
-
- {
- var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);
- defer ch.release();
-
- ch.add("1234");
-
- // There should be nothing in the cache
- testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit());
-
- digest1 = ch.final();
- }
- {
- var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);
- defer ch.release();
-
- ch.add("1234");
-
- digest2 = (try ch.hit()).?;
- }
-
- testing.expectEqual(digest1, digest2);
-}
-
-test "CacheHashes with files added after initial hash work" {
- if (std.Target.current.os.tag == .wasi) {
- // https://github.com/ziglang/zig/issues/5437
- return error.SkipZigTest;
- }
- const cwd = fs.cwd();
-
- const temp_file1 = "cache_hash_post_file_test1.txt";
- const temp_file2 = "cache_hash_post_file_test2.txt";
- const temp_manifest_dir = "cache_hash_post_file_manifest_dir";
-
- const ts1 = std.time.nanoTimestamp();
- try cwd.writeFile(temp_file1, "Hello, world!\n");
- try cwd.writeFile(temp_file2, "Hello world the second!\n");
-
- while (isProblematicTimestamp(ts1)) {
- std.time.sleep(1);
- }
-
- var digest1: [BASE64_DIGEST_LEN]u8 = undefined;
- var digest2: [BASE64_DIGEST_LEN]u8 = undefined;
- var digest3: [BASE64_DIGEST_LEN]u8 = undefined;
-
- {
- var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);
- defer ch.release();
-
- ch.add("1234");
- _ = try ch.addFile(temp_file1, null);
-
- // There should be nothing in the cache
- testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit());
-
- _ = try ch.addFilePost(temp_file2);
-
- digest1 = ch.final();
- }
- {
- var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);
- defer ch.release();
-
- ch.add("1234");
- _ = try ch.addFile(temp_file1, null);
-
- digest2 = (try ch.hit()).?;
- }
- testing.expect(mem.eql(u8, &digest1, &digest2));
-
- // Modify the file added after initial hash
- const ts2 = std.time.nanoTimestamp();
- try cwd.writeFile(temp_file2, "Hello world the second, updated\n");
-
- while (isProblematicTimestamp(ts2)) {
- std.time.sleep(1);
- }
-
- {
- var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir);
- defer ch.release();
-
- ch.add("1234");
- _ = try ch.addFile(temp_file1, null);
-
- // A file that we depend on has been updated, so the cache should not contain an entry for it
- testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit());
-
- _ = try ch.addFilePost(temp_file2);
-
- digest3 = ch.final();
- }
-
- testing.expect(!mem.eql(u8, &digest1, &digest3));
-
- try cwd.deleteTree(temp_manifest_dir);
- try cwd.deleteFile(temp_file1);
- try cwd.deleteFile(temp_file2);
-}
diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig
index 9219b05088..e706302ebd 100644
--- a/lib/std/child_process.zig
+++ b/lib/std/child_process.zig
@@ -213,7 +213,7 @@ pub const ChildProcess = struct {
const stdout_in = child.stdout.?.inStream();
const stderr_in = child.stderr.?.inStream();
- // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O).
+ // TODO https://github.com/ziglang/zig/issues/6343
const stdout = try stdout_in.readAllAlloc(args.allocator, args.max_output_bytes);
errdefer args.allocator.free(stdout);
const stderr = try stderr_in.readAllAlloc(args.allocator, args.max_output_bytes);
@@ -816,6 +816,13 @@ fn destroyPipe(pipe: [2]os.fd_t) void {
// Then the child exits.
fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {
writeIntFd(fd, @as(ErrInt, @errorToInt(err))) catch {};
+ // If we're linking libc, some naughty applications may have registered atexit handlers
+ // which we really do not want to run in the fork child. I caught LLVM doing this and
+ // it caused a deadlock instead of doing an exit syscall. In the words of Avril Lavigne,
+ // "Why'd you have to go and make things so complicated?"
+ if (std.Target.current.os.tag == .linux) {
+ std.os.linux.exit(1); // By-pass libc regardless of whether it is linked.
+ }
os.exit(1);
}
diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig
index 3a1ae599a0..fa69d51d4d 100644
--- a/lib/std/crypto.zig
+++ b/lib/std/crypto.zig
@@ -28,6 +28,8 @@ pub const aead = struct {
pub const Gimli = @import("crypto/gimli.zig").Aead;
pub const ChaCha20Poly1305 = chacha20.Chacha20Poly1305;
pub const XChaCha20Poly1305 = chacha20.XChacha20Poly1305;
+ pub const AEGIS128L = @import("crypto/aegis.zig").AEGIS128L;
+ pub const AEGIS256 = @import("crypto/aegis.zig").AEGIS256;
};
/// MAC functions requiring single-use secret keys.
@@ -35,12 +37,23 @@ pub const onetimeauth = struct {
pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305;
};
-/// A Key Derivation Function (KDF) is intended to turn a weak, human generated password into a
-/// strong key, suitable for cryptographic uses. It does this by salting and stretching the
-/// password. Salting injects non-secret random data, so that identical passwords will be converted
-/// into unique keys. Stretching applies a deliberately slow hashing function to frustrate
-/// brute-force guessing.
-pub const kdf = struct {
+/// A password hashing function derives a uniform key from low-entropy input material such as passwords.
+/// It is intentionally slow or expensive.
+///
+/// With the standard definition of a key derivation function, if a key space is small, an exhaustive search may be practical.
+/// Password hashing functions make exhaustive searches way slower or way more expensive, even when implemented on GPUs and ASICs, by using different, optionally combined strategies:
+///
+/// - Requiring a lot of computation cycles to complete
+/// - Requiring a lot of memory to complete
+/// - Requiring multiple CPU cores to complete
+/// - Requiring cache-local data to complete in reasonable time
+/// - Requiring large static tables
+/// - Avoiding precomputations and time/memory tradeoffs
+/// - Requiring multi-party computations
+/// - Combining the input material with random per-entry data (salts), application-specific contexts and keys
+///
+/// Password hashing functions must be used whenever sensitive data has to be directly derived from a password.
+pub const pwhash = struct {
pub const pbkdf2 = @import("crypto/pbkdf2.zig").pbkdf2;
};
@@ -48,6 +61,13 @@ pub const kdf = struct {
pub const core = struct {
pub const aes = @import("crypto/aes.zig");
pub const Gimli = @import("crypto/gimli.zig").State;
+
+ /// Modes are generic compositions to construct encryption/decryption functions from block ciphers and permutations.
+ ///
+ /// These modes are designed to be building blocks for higher-level constructions, and should generally not be used directly by applications, as they may not provide the expected properties and security guarantees.
+ ///
+ /// Most applications may want to use AEADs instead.
+ pub const modes = @import("crypto/modes.zig");
};
/// Elliptic-curve arithmetic.
@@ -100,6 +120,7 @@ test "crypto" {
_ = @import("crypto/gimli.zig");
_ = @import("crypto/hmac.zig");
_ = @import("crypto/md5.zig");
+ _ = @import("crypto/modes.zig");
_ = @import("crypto/pbkdf2.zig");
_ = @import("crypto/poly1305.zig");
_ = @import("crypto/sha1.zig");
diff --git a/lib/std/crypto/aegis.zig b/lib/std/crypto/aegis.zig
new file mode 100644
index 0000000000..cb9e4cabe9
--- /dev/null
+++ b/lib/std/crypto/aegis.zig
@@ -0,0 +1,447 @@
+const std = @import("std");
+const mem = std.mem;
+const assert = std.debug.assert;
+const AESBlock = std.crypto.core.aes.Block;
+
+const State128L = struct {
+ blocks: [8]AESBlock,
+
+ fn init(key: [16]u8, nonce: [16]u8) State128L {
+ const c1 = AESBlock.fromBytes(&[16]u8{ 0xdb, 0x3d, 0x18, 0x55, 0x6d, 0xc2, 0x2f, 0xf1, 0x20, 0x11, 0x31, 0x42, 0x73, 0xb5, 0x28, 0xdd });
+ const c2 = AESBlock.fromBytes(&[16]u8{ 0x0, 0x1, 0x01, 0x02, 0x03, 0x05, 0x08, 0x0d, 0x15, 0x22, 0x37, 0x59, 0x90, 0xe9, 0x79, 0x62 });
+ const key_block = AESBlock.fromBytes(&key);
+ const nonce_block = AESBlock.fromBytes(&nonce);
+ const blocks = [8]AESBlock{
+ key_block.xorBlocks(nonce_block),
+ c1,
+ c2,
+ c1,
+ key_block.xorBlocks(nonce_block),
+ key_block.xorBlocks(c2),
+ key_block.xorBlocks(c1),
+ key_block.xorBlocks(c2),
+ };
+ var state = State128L{ .blocks = blocks };
+ var i: usize = 0;
+ while (i < 10) : (i += 1) {
+ state.update(nonce_block, key_block);
+ }
+ return state;
+ }
+
+ inline fn update(state: *State128L, d1: AESBlock, d2: AESBlock) void {
+ const blocks = &state.blocks;
+ const tmp = blocks[7];
+ comptime var i: usize = 7;
+ inline while (i > 0) : (i -= 1) {
+ blocks[i] = blocks[i - 1].encrypt(blocks[i]);
+ }
+ blocks[0] = tmp.encrypt(blocks[0]);
+ blocks[0] = blocks[0].xorBlocks(d1);
+ blocks[4] = blocks[4].xorBlocks(d2);
+ }
+
+ fn enc(state: *State128L, dst: *[32]u8, src: *const [32]u8) void {
+ const blocks = &state.blocks;
+ const msg0 = AESBlock.fromBytes(src[0..16]);
+ const msg1 = AESBlock.fromBytes(src[16..32]);
+ var tmp0 = msg0.xorBlocks(blocks[6]).xorBlocks(blocks[1]);
+ var tmp1 = msg1.xorBlocks(blocks[2]).xorBlocks(blocks[5]);
+ tmp0 = tmp0.xorBlocks(blocks[2].andBlocks(blocks[3]));
+ tmp1 = tmp1.xorBlocks(blocks[6].andBlocks(blocks[7]));
+ dst[0..16].* = tmp0.toBytes();
+ dst[16..32].* = tmp1.toBytes();
+ state.update(msg0, msg1);
+ }
+
+ fn dec(state: *State128L, dst: *[32]u8, src: *const [32]u8) void {
+ const blocks = &state.blocks;
+ var msg0 = AESBlock.fromBytes(src[0..16]).xorBlocks(blocks[6]).xorBlocks(blocks[1]);
+ var msg1 = AESBlock.fromBytes(src[16..32]).xorBlocks(blocks[2]).xorBlocks(blocks[5]);
+ msg0 = msg0.xorBlocks(blocks[2].andBlocks(blocks[3]));
+ msg1 = msg1.xorBlocks(blocks[6].andBlocks(blocks[7]));
+ dst[0..16].* = msg0.toBytes();
+ dst[16..32].* = msg1.toBytes();
+ state.update(msg0, msg1);
+ }
+
+ fn mac(state: *State128L, adlen: usize, mlen: usize) [16]u8 {
+ const blocks = &state.blocks;
+ var sizes: [16]u8 = undefined;
+ mem.writeIntLittle(u64, sizes[0..8], adlen * 8);
+ mem.writeIntLittle(u64, sizes[8..16], mlen * 8);
+ const tmp = AESBlock.fromBytes(&sizes).xorBlocks(blocks[2]);
+ var i: usize = 0;
+ while (i < 7) : (i += 1) {
+ state.update(tmp, tmp);
+ }
+ return blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4]).
+ xorBlocks(blocks[5]).xorBlocks(blocks[6]).toBytes();
+ }
+};
+
+/// AEGIS is a very fast authenticated encryption system built on top of the core AES function.
+///
+/// The 128L variant of AEGIS has a 128 bit key, a 128 bit nonce, and processes 256 bit message blocks.
+/// It was designed to fully exploit the parallelism and built-in AES support of recent Intel and ARM CPUs.
+///
+/// https://competitions.cr.yp.to/round3/aegisv11.pdf
+pub const AEGIS128L = struct {
+ pub const tag_length = 16;
+ pub const nonce_length = 16;
+ pub const key_length = 16;
+
+ /// c: ciphertext: output buffer should be of size m.len
+ /// tag: authentication tag: output MAC
+ /// m: message
+ /// ad: Associated Data
+ /// npub: public nonce
+ /// k: private key
+ pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void {
+ assert(c.len == m.len);
+ var state = State128L.init(key, npub);
+ var src: [32]u8 align(16) = undefined;
+ var dst: [32]u8 align(16) = undefined;
+ var i: usize = 0;
+ while (i + 32 <= ad.len) : (i += 32) {
+ state.enc(&dst, ad[i..][0..32]);
+ }
+ if (ad.len % 32 != 0) {
+ mem.set(u8, src[0..], 0);
+ mem.copy(u8, src[0 .. ad.len % 32], ad[i .. i + ad.len % 32]);
+ state.enc(&dst, &src);
+ }
+ i = 0;
+ while (i + 32 <= m.len) : (i += 32) {
+ state.enc(c[i..][0..32], m[i..][0..32]);
+ }
+ if (m.len % 32 != 0) {
+ mem.set(u8, src[0..], 0);
+ mem.copy(u8, src[0 .. m.len % 32], m[i .. i + m.len % 32]);
+ state.enc(&dst, &src);
+ mem.copy(u8, c[i .. i + m.len % 32], dst[0 .. m.len % 32]);
+ }
+ tag.* = state.mac(ad.len, m.len);
+ }
+
+ /// m: message: output buffer should be of size c.len
+ /// c: ciphertext
+ /// tag: authentication tag
+ /// 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) !void {
+ assert(c.len == m.len);
+ var state = State128L.init(key, npub);
+ var src: [32]u8 align(16) = undefined;
+ var dst: [32]u8 align(16) = undefined;
+ var i: usize = 0;
+ while (i + 32 <= ad.len) : (i += 32) {
+ state.enc(&dst, ad[i..][0..32]);
+ }
+ if (ad.len % 32 != 0) {
+ mem.set(u8, src[0..], 0);
+ mem.copy(u8, src[0 .. ad.len % 32], ad[i .. i + ad.len % 32]);
+ state.enc(&dst, &src);
+ }
+ i = 0;
+ while (i + 32 <= m.len) : (i += 32) {
+ state.dec(m[i..][0..32], c[i..][0..32]);
+ }
+ if (m.len % 32 != 0) {
+ mem.set(u8, src[0..], 0);
+ mem.copy(u8, src[0 .. m.len % 32], c[i .. i + m.len % 32]);
+ state.dec(&dst, &src);
+ mem.copy(u8, m[i .. i + m.len % 32], dst[0 .. m.len % 32]);
+ mem.set(u8, dst[0 .. m.len % 32], 0);
+ const blocks = &state.blocks;
+ blocks[0] = blocks[0].xorBlocks(AESBlock.fromBytes(dst[0..16]));
+ blocks[4] = blocks[4].xorBlocks(AESBlock.fromBytes(dst[16..32]));
+ }
+ const computed_tag = state.mac(ad.len, m.len);
+ var acc: u8 = 0;
+ for (computed_tag) |_, j| {
+ acc |= (computed_tag[j] ^ tag[j]);
+ }
+ if (acc != 0) {
+ mem.set(u8, m, 0xaa);
+ return error.AuthenticationFailed;
+ }
+ }
+};
+
+const State256 = struct {
+ blocks: [6]AESBlock,
+
+ fn init(key: [32]u8, nonce: [32]u8) State256 {
+ const c1 = AESBlock.fromBytes(&[16]u8{ 0xdb, 0x3d, 0x18, 0x55, 0x6d, 0xc2, 0x2f, 0xf1, 0x20, 0x11, 0x31, 0x42, 0x73, 0xb5, 0x28, 0xdd });
+ const c2 = AESBlock.fromBytes(&[16]u8{ 0x0, 0x1, 0x01, 0x02, 0x03, 0x05, 0x08, 0x0d, 0x15, 0x22, 0x37, 0x59, 0x90, 0xe9, 0x79, 0x62 });
+ const key_block1 = AESBlock.fromBytes(key[0..16]);
+ const key_block2 = AESBlock.fromBytes(key[16..32]);
+ const nonce_block1 = AESBlock.fromBytes(nonce[0..16]);
+ const nonce_block2 = AESBlock.fromBytes(nonce[16..32]);
+ const kxn1 = key_block1.xorBlocks(nonce_block1);
+ const kxn2 = key_block2.xorBlocks(nonce_block2);
+ const blocks = [6]AESBlock{
+ kxn1,
+ kxn2,
+ c1,
+ c2,
+ key_block1.xorBlocks(c2),
+ key_block2.xorBlocks(c1),
+ };
+ var state = State256{ .blocks = blocks };
+ var i: usize = 0;
+ while (i < 4) : (i += 1) {
+ state.update(key_block1);
+ state.update(key_block2);
+ state.update(kxn1);
+ state.update(kxn2);
+ }
+ return state;
+ }
+
+ inline fn update(state: *State256, d: AESBlock) void {
+ const blocks = &state.blocks;
+ const tmp = blocks[5].encrypt(blocks[0]);
+ comptime var i: usize = 5;
+ inline while (i > 0) : (i -= 1) {
+ blocks[i] = blocks[i - 1].encrypt(blocks[i]);
+ }
+ blocks[0] = tmp.xorBlocks(d);
+ }
+
+ fn enc(state: *State256, dst: *[16]u8, src: *const [16]u8) void {
+ const blocks = &state.blocks;
+ const msg = AESBlock.fromBytes(src);
+ var tmp = msg.xorBlocks(blocks[5]).xorBlocks(blocks[4]).xorBlocks(blocks[1]);
+ tmp = tmp.xorBlocks(blocks[2].andBlocks(blocks[3]));
+ dst.* = tmp.toBytes();
+ state.update(msg);
+ }
+
+ fn dec(state: *State256, dst: *[16]u8, src: *const [16]u8) void {
+ const blocks = &state.blocks;
+ var msg = AESBlock.fromBytes(src).xorBlocks(blocks[5]).xorBlocks(blocks[4]).xorBlocks(blocks[1]);
+ msg = msg.xorBlocks(blocks[2].andBlocks(blocks[3]));
+ dst.* = msg.toBytes();
+ state.update(msg);
+ }
+
+ fn mac(state: *State256, adlen: usize, mlen: usize) [16]u8 {
+ const blocks = &state.blocks;
+ var sizes: [16]u8 = undefined;
+ mem.writeIntLittle(u64, sizes[0..8], adlen * 8);
+ mem.writeIntLittle(u64, sizes[8..16], mlen * 8);
+ const tmp = AESBlock.fromBytes(&sizes).xorBlocks(blocks[3]);
+ var i: usize = 0;
+ while (i < 7) : (i += 1) {
+ state.update(tmp);
+ }
+ return blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4]).
+ xorBlocks(blocks[5]).toBytes();
+ }
+};
+
+/// AEGIS is a very fast authenticated encryption system built on top of the core AES function.
+///
+/// The 256 bit variant of AEGIS has a 256 bit key, a 256 bit nonce, and processes 128 bit message blocks.
+///
+/// https://competitions.cr.yp.to/round3/aegisv11.pdf
+pub const AEGIS256 = struct {
+ pub const tag_length = 16;
+ pub const nonce_length = 32;
+ pub const key_length = 32;
+
+ /// c: ciphertext: output buffer should be of size m.len
+ /// tag: authentication tag: output MAC
+ /// m: message
+ /// ad: Associated Data
+ /// npub: public nonce
+ /// k: private key
+ pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void {
+ assert(c.len == m.len);
+ var state = State256.init(key, npub);
+ var src: [16]u8 align(16) = undefined;
+ var dst: [16]u8 align(16) = undefined;
+ var i: usize = 0;
+ while (i + 16 <= ad.len) : (i += 16) {
+ state.enc(&dst, ad[i..][0..16]);
+ }
+ if (ad.len % 16 != 0) {
+ mem.set(u8, src[0..], 0);
+ mem.copy(u8, src[0 .. ad.len % 16], ad[i .. i + ad.len % 16]);
+ state.enc(&dst, &src);
+ }
+ i = 0;
+ while (i + 16 <= m.len) : (i += 16) {
+ state.enc(c[i..][0..16], m[i..][0..16]);
+ }
+ if (m.len % 16 != 0) {
+ mem.set(u8, src[0..], 0);
+ mem.copy(u8, src[0 .. m.len % 16], m[i .. i + m.len % 16]);
+ state.enc(&dst, &src);
+ mem.copy(u8, c[i .. i + m.len % 16], dst[0 .. m.len % 16]);
+ }
+ tag.* = state.mac(ad.len, m.len);
+ }
+
+ /// m: message: output buffer should be of size c.len
+ /// c: ciphertext
+ /// tag: authentication tag
+ /// 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) !void {
+ assert(c.len == m.len);
+ var state = State256.init(key, npub);
+ var src: [16]u8 align(16) = undefined;
+ var dst: [16]u8 align(16) = undefined;
+ var i: usize = 0;
+ while (i + 16 <= ad.len) : (i += 16) {
+ state.enc(&dst, ad[i..][0..16]);
+ }
+ if (ad.len % 16 != 0) {
+ mem.set(u8, src[0..], 0);
+ mem.copy(u8, src[0 .. ad.len % 16], ad[i .. i + ad.len % 16]);
+ state.enc(&dst, &src);
+ }
+ i = 0;
+ while (i + 16 <= m.len) : (i += 16) {
+ state.dec(m[i..][0..16], c[i..][0..16]);
+ }
+ if (m.len % 16 != 0) {
+ mem.set(u8, src[0..], 0);
+ mem.copy(u8, src[0 .. m.len % 16], c[i .. i + m.len % 16]);
+ state.dec(&dst, &src);
+ mem.copy(u8, m[i .. i + m.len % 16], dst[0 .. m.len % 16]);
+ mem.set(u8, dst[0 .. m.len % 16], 0);
+ const blocks = &state.blocks;
+ blocks[0] = blocks[0].xorBlocks(AESBlock.fromBytes(&dst));
+ }
+ const computed_tag = state.mac(ad.len, m.len);
+ var acc: u8 = 0;
+ for (computed_tag) |_, j| {
+ acc |= (computed_tag[j] ^ tag[j]);
+ }
+ if (acc != 0) {
+ mem.set(u8, m, 0xaa);
+ return error.AuthenticationFailed;
+ }
+ }
+};
+
+const htest = @import("test.zig");
+const testing = std.testing;
+
+test "AEGIS128L test vector 1" {
+ const key: [AEGIS128L.key_length]u8 = [_]u8{ 0x10, 0x01 } ++ [_]u8{0x00} ** 14;
+ const nonce: [AEGIS128L.nonce_length]u8 = [_]u8{ 0x10, 0x00, 0x02 } ++ [_]u8{0x00} ** 13;
+ const ad = [8]u8{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
+ const m = [32]u8{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f };
+ var c: [m.len]u8 = undefined;
+ var m2: [m.len]u8 = undefined;
+ var tag: [AEGIS128L.tag_length]u8 = undefined;
+
+ AEGIS128L.encrypt(&c, &tag, &m, &ad, nonce, key);
+ try AEGIS128L.decrypt(&m2, &c, tag, &ad, nonce, key);
+ testing.expectEqualSlices(u8, &m, &m2);
+
+ htest.assertEqual("79d94593d8c2119d7e8fd9b8fc77845c5c077a05b2528b6ac54b563aed8efe84", &c);
+ htest.assertEqual("cc6f3372f6aa1bb82388d695c3962d9a", &tag);
+
+ c[0] +%= 1;
+ testing.expectError(error.AuthenticationFailed, AEGIS128L.decrypt(&m2, &c, tag, &ad, nonce, key));
+ c[0] -%= 1;
+ tag[0] +%= 1;
+ testing.expectError(error.AuthenticationFailed, AEGIS128L.decrypt(&m2, &c, tag, &ad, nonce, key));
+}
+
+test "AEGIS128L test vector 2" {
+ const key: [AEGIS128L.key_length]u8 = [_]u8{0x00} ** 16;
+ const nonce: [AEGIS128L.nonce_length]u8 = [_]u8{0x00} ** 16;
+ const ad = [_]u8{};
+ const m = [_]u8{0x00} ** 16;
+ var c: [m.len]u8 = undefined;
+ var m2: [m.len]u8 = undefined;
+ var tag: [AEGIS128L.tag_length]u8 = undefined;
+
+ AEGIS128L.encrypt(&c, &tag, &m, &ad, nonce, key);
+ try AEGIS128L.decrypt(&m2, &c, tag, &ad, nonce, key);
+ testing.expectEqualSlices(u8, &m, &m2);
+
+ htest.assertEqual("41de9000a7b5e40e2d68bb64d99ebb19", &c);
+ htest.assertEqual("f4d997cc9b94227ada4fe4165422b1c8", &tag);
+}
+
+test "AEGIS128L test vector 3" {
+ const key: [AEGIS128L.key_length]u8 = [_]u8{0x00} ** 16;
+ const nonce: [AEGIS128L.nonce_length]u8 = [_]u8{0x00} ** 16;
+ const ad = [_]u8{};
+ const m = [_]u8{};
+ var c: [m.len]u8 = undefined;
+ var m2: [m.len]u8 = undefined;
+ var tag: [AEGIS128L.tag_length]u8 = undefined;
+
+ AEGIS128L.encrypt(&c, &tag, &m, &ad, nonce, key);
+ try AEGIS128L.decrypt(&m2, &c, tag, &ad, nonce, key);
+ testing.expectEqualSlices(u8, &m, &m2);
+
+ htest.assertEqual("83cc600dc4e3e7e62d4055826174f149", &tag);
+}
+
+test "AEGIS256 test vector 1" {
+ const key: [AEGIS256.key_length]u8 = [_]u8{ 0x10, 0x01 } ++ [_]u8{0x00} ** 30;
+ const nonce: [AEGIS256.nonce_length]u8 = [_]u8{ 0x10, 0x00, 0x02 } ++ [_]u8{0x00} ** 29;
+ const ad = [8]u8{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
+ const m = [32]u8{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f };
+ var c: [m.len]u8 = undefined;
+ var m2: [m.len]u8 = undefined;
+ var tag: [AEGIS256.tag_length]u8 = undefined;
+
+ AEGIS256.encrypt(&c, &tag, &m, &ad, nonce, key);
+ try AEGIS256.decrypt(&m2, &c, tag, &ad, nonce, key);
+ testing.expectEqualSlices(u8, &m, &m2);
+
+ htest.assertEqual("f373079ed84b2709faee373584585d60accd191db310ef5d8b11833df9dec711", &c);
+ htest.assertEqual("8d86f91ee606e9ff26a01b64ccbdd91d", &tag);
+
+ c[0] +%= 1;
+ testing.expectError(error.AuthenticationFailed, AEGIS256.decrypt(&m2, &c, tag, &ad, nonce, key));
+ c[0] -%= 1;
+ tag[0] +%= 1;
+ testing.expectError(error.AuthenticationFailed, AEGIS256.decrypt(&m2, &c, tag, &ad, nonce, key));
+}
+
+test "AEGIS256 test vector 2" {
+ const key: [AEGIS256.key_length]u8 = [_]u8{0x00} ** 32;
+ const nonce: [AEGIS256.nonce_length]u8 = [_]u8{0x00} ** 32;
+ const ad = [_]u8{};
+ const m = [_]u8{0x00} ** 16;
+ var c: [m.len]u8 = undefined;
+ var m2: [m.len]u8 = undefined;
+ var tag: [AEGIS256.tag_length]u8 = undefined;
+
+ AEGIS256.encrypt(&c, &tag, &m, &ad, nonce, key);
+ try AEGIS256.decrypt(&m2, &c, tag, &ad, nonce, key);
+ testing.expectEqualSlices(u8, &m, &m2);
+
+ htest.assertEqual("b98f03a947807713d75a4fff9fc277a6", &c);
+ htest.assertEqual("478f3b50dc478ef7d5cf2d0f7cc13180", &tag);
+}
+
+test "AEGIS256 test vector 3" {
+ const key: [AEGIS256.key_length]u8 = [_]u8{0x00} ** 32;
+ const nonce: [AEGIS256.nonce_length]u8 = [_]u8{0x00} ** 32;
+ const ad = [_]u8{};
+ const m = [_]u8{};
+ var c: [m.len]u8 = undefined;
+ var m2: [m.len]u8 = undefined;
+ var tag: [AEGIS256.tag_length]u8 = undefined;
+
+ AEGIS256.encrypt(&c, &tag, &m, &ad, nonce, key);
+ try AEGIS256.decrypt(&m2, &c, tag, &ad, nonce, key);
+ testing.expectEqualSlices(u8, &m, &m2);
+
+ htest.assertEqual("f7a0878f68bd083e8065354071fc27c3", &tag);
+}
diff --git a/lib/std/crypto/aes.zig b/lib/std/crypto/aes.zig
index 2174fbdd4f..7c509b297f 100644
--- a/lib/std/crypto/aes.zig
+++ b/lib/std/crypto/aes.zig
@@ -3,249 +3,44 @@
// 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.
-// Based on Go stdlib implementation
const std = @import("../std.zig");
-const mem = std.mem;
const testing = std.testing;
+const builtin = std.builtin;
-// Apply sbox0 to each byte in w.
-fn subw(w: u32) u32 {
- return @as(u32, sbox0[w >> 24]) << 24 | @as(u32, sbox0[w >> 16 & 0xff]) << 16 | @as(u32, sbox0[w >> 8 & 0xff]) << 8 | @as(u32, sbox0[w & 0xff]);
-}
+const has_aesni = comptime std.Target.x86.featureSetHas(std.Target.current.cpu.features, .aes);
+const has_avx = comptime std.Target.x86.featureSetHas(std.Target.current.cpu.features, .avx);
+const impl = if (std.Target.current.cpu.arch == .x86_64 and has_aesni and has_avx) @import("aes/aesni.zig") else @import("aes/soft.zig");
-fn rotw(w: u32) u32 {
- return w << 8 | w >> 24;
-}
-
-// Encrypt one block from src into dst, using the expanded key xk.
-fn encryptBlock(xk: []const u32, dst: []u8, src: []const u8) void {
- var s0 = mem.readIntBig(u32, src[0..4]);
- var s1 = mem.readIntBig(u32, src[4..8]);
- var s2 = mem.readIntBig(u32, src[8..12]);
- var s3 = mem.readIntBig(u32, src[12..16]);
-
- // First round just XORs input with key.
- s0 ^= xk[0];
- s1 ^= xk[1];
- s2 ^= xk[2];
- s3 ^= xk[3];
-
- // Middle rounds shuffle using tables.
- // Number of rounds is set by length of expanded key.
- var nr = xk.len / 4 - 2; // - 2: one above, one more below
- var k: usize = 4;
- var t0: u32 = undefined;
- var t1: u32 = undefined;
- var t2: u32 = undefined;
- var t3: u32 = undefined;
- var r: usize = 0;
- while (r < nr) : (r += 1) {
- t0 = xk[k + 0] ^ te0[@truncate(u8, s0 >> 24)] ^ te1[@truncate(u8, s1 >> 16)] ^ te2[@truncate(u8, s2 >> 8)] ^ te3[@truncate(u8, s3)];
- t1 = xk[k + 1] ^ te0[@truncate(u8, s1 >> 24)] ^ te1[@truncate(u8, s2 >> 16)] ^ te2[@truncate(u8, s3 >> 8)] ^ te3[@truncate(u8, s0)];
- t2 = xk[k + 2] ^ te0[@truncate(u8, s2 >> 24)] ^ te1[@truncate(u8, s3 >> 16)] ^ te2[@truncate(u8, s0 >> 8)] ^ te3[@truncate(u8, s1)];
- t3 = xk[k + 3] ^ te0[@truncate(u8, s3 >> 24)] ^ te1[@truncate(u8, s0 >> 16)] ^ te2[@truncate(u8, s1 >> 8)] ^ te3[@truncate(u8, s2)];
- k += 4;
- s0 = t0;
- s1 = t1;
- s2 = t2;
- s3 = t3;
- }
-
- // Last round uses s-box directly and XORs to produce output.
- s0 = @as(u32, sbox0[t0 >> 24]) << 24 | @as(u32, sbox0[t1 >> 16 & 0xff]) << 16 | @as(u32, sbox0[t2 >> 8 & 0xff]) << 8 | @as(u32, sbox0[t3 & 0xff]);
- s1 = @as(u32, sbox0[t1 >> 24]) << 24 | @as(u32, sbox0[t2 >> 16 & 0xff]) << 16 | @as(u32, sbox0[t3 >> 8 & 0xff]) << 8 | @as(u32, sbox0[t0 & 0xff]);
- s2 = @as(u32, sbox0[t2 >> 24]) << 24 | @as(u32, sbox0[t3 >> 16 & 0xff]) << 16 | @as(u32, sbox0[t0 >> 8 & 0xff]) << 8 | @as(u32, sbox0[t1 & 0xff]);
- s3 = @as(u32, sbox0[t3 >> 24]) << 24 | @as(u32, sbox0[t0 >> 16 & 0xff]) << 16 | @as(u32, sbox0[t1 >> 8 & 0xff]) << 8 | @as(u32, sbox0[t2 & 0xff]);
-
- s0 ^= xk[k + 0];
- s1 ^= xk[k + 1];
- s2 ^= xk[k + 2];
- s3 ^= xk[k + 3];
-
- mem.writeIntBig(u32, dst[0..4], s0);
- mem.writeIntBig(u32, dst[4..8], s1);
- mem.writeIntBig(u32, dst[8..12], s2);
- mem.writeIntBig(u32, dst[12..16], s3);
-}
-
-// Decrypt one block from src into dst, using the expanded key xk.
-pub fn decryptBlock(xk: []const u32, dst: []u8, src: []const u8) void {
- var s0 = mem.readIntBig(u32, src[0..4]);
- var s1 = mem.readIntBig(u32, src[4..8]);
- var s2 = mem.readIntBig(u32, src[8..12]);
- var s3 = mem.readIntBig(u32, src[12..16]);
-
- // First round just XORs input with key.
- s0 ^= xk[0];
- s1 ^= xk[1];
- s2 ^= xk[2];
- s3 ^= xk[3];
-
- // Middle rounds shuffle using tables.
- // Number of rounds is set by length of expanded key.
- var nr = xk.len / 4 - 2; // - 2: one above, one more below
- var k: usize = 4;
- var t0: u32 = undefined;
- var t1: u32 = undefined;
- var t2: u32 = undefined;
- var t3: u32 = undefined;
- var r: usize = 0;
- while (r < nr) : (r += 1) {
- t0 = xk[k + 0] ^ td0[@truncate(u8, s0 >> 24)] ^ td1[@truncate(u8, s3 >> 16)] ^ td2[@truncate(u8, s2 >> 8)] ^ td3[@truncate(u8, s1)];
- t1 = xk[k + 1] ^ td0[@truncate(u8, s1 >> 24)] ^ td1[@truncate(u8, s0 >> 16)] ^ td2[@truncate(u8, s3 >> 8)] ^ td3[@truncate(u8, s2)];
- t2 = xk[k + 2] ^ td0[@truncate(u8, s2 >> 24)] ^ td1[@truncate(u8, s1 >> 16)] ^ td2[@truncate(u8, s0 >> 8)] ^ td3[@truncate(u8, s3)];
- t3 = xk[k + 3] ^ td0[@truncate(u8, s3 >> 24)] ^ td1[@truncate(u8, s2 >> 16)] ^ td2[@truncate(u8, s1 >> 8)] ^ td3[@truncate(u8, s0)];
- k += 4;
- s0 = t0;
- s1 = t1;
- s2 = t2;
- s3 = t3;
- }
-
- // Last round uses s-box directly and XORs to produce output.
- s0 = @as(u32, sbox1[t0 >> 24]) << 24 | @as(u32, sbox1[t3 >> 16 & 0xff]) << 16 | @as(u32, sbox1[t2 >> 8 & 0xff]) << 8 | @as(u32, sbox1[t1 & 0xff]);
- s1 = @as(u32, sbox1[t1 >> 24]) << 24 | @as(u32, sbox1[t0 >> 16 & 0xff]) << 16 | @as(u32, sbox1[t3 >> 8 & 0xff]) << 8 | @as(u32, sbox1[t2 & 0xff]);
- s2 = @as(u32, sbox1[t2 >> 24]) << 24 | @as(u32, sbox1[t1 >> 16 & 0xff]) << 16 | @as(u32, sbox1[t0 >> 8 & 0xff]) << 8 | @as(u32, sbox1[t3 & 0xff]);
- s3 = @as(u32, sbox1[t3 >> 24]) << 24 | @as(u32, sbox1[t2 >> 16 & 0xff]) << 16 | @as(u32, sbox1[t1 >> 8 & 0xff]) << 8 | @as(u32, sbox1[t0 & 0xff]);
-
- s0 ^= xk[k + 0];
- s1 ^= xk[k + 1];
- s2 ^= xk[k + 2];
- s3 ^= xk[k + 3];
-
- mem.writeIntBig(u32, dst[0..4], s0);
- mem.writeIntBig(u32, dst[4..8], s1);
- mem.writeIntBig(u32, dst[8..12], s2);
- mem.writeIntBig(u32, dst[12..16], s3);
-}
-
-fn xorBytes(dst: []u8, a: []const u8, b: []const u8) usize {
- var n = std.math.min(dst.len, std.math.min(a.len, b.len));
- for (dst[0..n]) |_, i| {
- dst[i] = a[i] ^ b[i];
- }
- return n;
-}
-
-pub const AES128 = AES(128);
-pub const AES256 = AES(256);
-
-fn AES(comptime keysize: usize) type {
- return struct {
- const Self = @This();
-
- pub const Encrypt = AESEncrypt(keysize);
- pub const Decrypt = AESDecrypt(keysize);
-
- const nn = (keysize / 8) + 28;
- enc: Encrypt,
- dec: Decrypt,
-
- pub fn init(key: [keysize / 8]u8) Self {
- var ctx: Self = undefined;
- ctx.enc = Encrypt.init(key);
- ctx.dec = ctx.enc.toDecrypt();
- return ctx;
- }
-
- pub fn encrypt(ctx: Self, dst: []u8, src: []const u8) void {
- ctx.enc.encrypt(dst, src);
- }
- pub fn decrypt(ctx: Self, dst: []u8, src: []const u8) void {
- ctx.dec.decrypt(dst, src);
- }
- pub fn ctr(ctx: Self, dst: []u8, src: []const u8, iv: [16]u8) void {
- ctx.enc.ctr(dst, src, iv);
- }
- };
-}
-
-fn AESEncrypt(comptime keysize: usize) type {
- return struct {
- const Self = @This();
-
- const Decrypt = AESDecrypt(keysize);
-
- const nn = (keysize / 8) + 28;
- enc: [nn]u32,
-
- pub fn init(key: [keysize / 8]u8) Self {
- var ctx: Self = undefined;
- expandKeyEncrypt(&key, ctx.enc[0..]);
- return ctx;
- }
-
- pub fn toDecrypt(ctx: Self) Decrypt {
- var dec: Decrypt = undefined;
- expandKeyDecrypt(ctx.enc[0..], dec.dec[0..]);
- return dec;
- }
-
- pub fn encrypt(ctx: Self, dst: []u8, src: []const u8) void {
- encryptBlock(ctx.enc[0..], dst, src);
- }
- pub fn ctr(ctx: Self, dst: []u8, src: []const u8, iv: [16]u8) void {
- std.debug.assert(dst.len >= src.len);
-
- var keystream: [16]u8 = undefined;
- var ctrbuf = iv;
- var n: usize = 0;
- while (n < src.len) {
- ctx.encrypt(keystream[0..], ctrbuf[0..]);
- var ctr_i = std.mem.readIntBig(u128, ctrbuf[0..]);
- std.mem.writeIntBig(u128, ctrbuf[0..], ctr_i +% 1);
-
- n += xorBytes(dst[n..], src[n..], &keystream);
- }
- }
- };
-}
-
-fn AESDecrypt(comptime keysize: usize) type {
- return struct {
- const Self = @This();
-
- const nn = (keysize / 8) + 28;
- dec: [nn]u32,
-
- pub fn init(key: [keysize / 8]u8) Self {
- var ctx: Self = undefined;
- var enc: [nn]u32 = undefined;
- expandKeyEncrypt(key[0..], enc[0..]);
- expandKeyDecrypt(enc[0..], ctx.dec[0..]);
- return ctx;
- }
-
- pub fn decrypt(ctx: Self, dst: []u8, src: []const u8) void {
- decryptBlock(ctx.dec[0..], dst, src);
- }
- };
-}
+pub const Block = impl.Block;
+pub const AESEncryptCtx = impl.AESEncryptCtx;
+pub const AESDecryptCtx = impl.AESDecryptCtx;
+pub const AES128 = impl.AES128;
+pub const AES256 = impl.AES256;
test "ctr" {
// NIST SP 800-38A pp 55-58
- {
- const key = [_]u8{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
- const iv = [_]u8{ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff };
- const in = [_]u8{
- 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
- 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
- 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
- 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
- };
- const exp_out = [_]u8{
- 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
- 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff, 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
- 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
- 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1, 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee,
- };
+ const ctr = @import("modes.zig").ctr;
- var out: [exp_out.len]u8 = undefined;
- var aes = AES128.init(key);
- aes.ctr(out[0..], in[0..], iv);
- testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
- }
+ const key = [_]u8{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
+ const iv = [_]u8{ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff };
+ const in = [_]u8{
+ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
+ };
+ const exp_out = [_]u8{
+ 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+ 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff, 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
+ 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
+ 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1, 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee,
+ };
+
+ var out: [exp_out.len]u8 = undefined;
+ var ctx = AES128.initEnc(key);
+ ctr(AESEncryptCtx(AES128), ctx, out[0..], in[0..], iv, builtin.Endian.Big);
+ testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
}
test "encrypt" {
@@ -256,8 +51,8 @@ test "encrypt" {
const exp_out = [_]u8{ 0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 0x0b, 0x32 };
var out: [exp_out.len]u8 = undefined;
- var aes = AES128.init(key);
- aes.encrypt(out[0..], in[0..]);
+ var ctx = AES128.initEnc(key);
+ ctx.encrypt(out[0..], in[0..]);
testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
}
@@ -271,8 +66,8 @@ test "encrypt" {
const exp_out = [_]u8{ 0x8e, 0xa2, 0xb7, 0xca, 0x51, 0x67, 0x45, 0xbf, 0xea, 0xfc, 0x49, 0x90, 0x4b, 0x49, 0x60, 0x89 };
var out: [exp_out.len]u8 = undefined;
- var aes = AES256.init(key);
- aes.encrypt(out[0..], in[0..]);
+ var ctx = AES256.initEnc(key);
+ ctx.encrypt(out[0..], in[0..]);
testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
}
}
@@ -285,8 +80,8 @@ test "decrypt" {
const exp_out = [_]u8{ 0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34 };
var out: [exp_out.len]u8 = undefined;
- var aes = AES128.init(key);
- aes.decrypt(out[0..], in[0..]);
+ var ctx = AES128.initDec(key);
+ ctx.decrypt(out[0..], in[0..]);
testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
}
@@ -300,413 +95,52 @@ test "decrypt" {
const exp_out = [_]u8{ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
var out: [exp_out.len]u8 = undefined;
- var aes = AES256.init(key);
- aes.decrypt(out[0..], in[0..]);
+ var ctx = AES256.initDec(key);
+ ctx.decrypt(out[0..], in[0..]);
testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
}
}
-// Key expansion algorithm. See FIPS-197, Figure 11.
-fn expandKeyEncrypt(key: []const u8, enc: []u32) void {
- var i: usize = 0;
- var nk = key.len / 4;
- while (i < nk) : (i += 1) {
- enc[i] = mem.readIntBig(u32, key[4 * i ..][0..4]);
- }
- while (i < enc.len) : (i += 1) {
- var t = enc[i - 1];
- if (i % nk == 0) {
- t = subw(rotw(t)) ^ (@as(u32, powx[i / nk - 1]) << 24);
- } else if (nk > 6 and i % nk == 4) {
- t = subw(t);
- }
- enc[i] = enc[i - nk] ^ t;
- }
-}
-
-fn expandKeyDecrypt(enc: []const u32, dec: []u32) void {
- var i: usize = 0;
- var n = enc.len;
- while (i < n) : (i += 4) {
- var ei = n - i - 4;
- var j: usize = 0;
- while (j < 4) : (j += 1) {
- var x = enc[ei + j];
- if (i > 0 and i + 4 < n) {
- x = td0[sbox0[x >> 24]] ^ td1[sbox0[x >> 16 & 0xff]] ^ td2[sbox0[x >> 8 & 0xff]] ^ td3[sbox0[x & 0xff]];
- }
- dec[i + j] = x;
- }
- }
-}
-
-test "expand key" {
+test "expand 128-bit key" {
const key = [_]u8{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
- const exp_enc = [_]u32{
- 0x2b7e1516, 0x28aed2a6, 0xabf71588, 0x09cf4f3c,
- 0xa0fafe17, 0x88542cb1, 0x23a33939, 0x2a6c7605,
- 0xf2c295f2, 0x7a96b943, 0x5935807a, 0x7359f67f,
- 0x3d80477d, 0x4716fe3e, 0x1e237e44, 0x6d7a883b,
- 0xef44a541, 0xa8525b7f, 0xb671253b, 0xdb0bad00,
- 0xd4d1c6f8, 0x7c839d87, 0xcaf2b8bc, 0x11f915bc,
- 0x6d88a37a, 0x110b3efd, 0xdbf98641, 0xca0093fd,
- 0x4e54f70e, 0x5f5fc9f3, 0x84a64fb2, 0x4ea6dc4f,
- 0xead27321, 0xb58dbad2, 0x312bf560, 0x7f8d292f,
- 0xac7766f3, 0x19fadc21, 0x28d12941, 0x575c006e,
- 0xd014f9a8, 0xc9ee2589, 0xe13f0cc8, 0xb6630ca6,
+ const exp_enc = [_]*const [32:0]u8{
+ "2b7e151628aed2a6abf7158809cf4f3c", "a0fafe1788542cb123a339392a6c7605", "f2c295f27a96b9435935807a7359f67f", "3d80477d4716fe3e1e237e446d7a883b", "ef44a541a8525b7fb671253bdb0bad00", "d4d1c6f87c839d87caf2b8bc11f915bc", "6d88a37a110b3efddbf98641ca0093fd", "4e54f70e5f5fc9f384a64fb24ea6dc4f", "ead27321b58dbad2312bf5607f8d292f", "ac7766f319fadc2128d12941575c006e", "d014f9a8c9ee2589e13f0cc8b6630ca6",
};
- const exp_dec = [_]u32{
- 0xd014f9a8, 0xc9ee2589, 0xe13f0cc8, 0xb6630ca6,
- 0xc7b5a63, 0x1319eafe, 0xb0398890, 0x664cfbb4,
- 0xdf7d925a, 0x1f62b09d, 0xa320626e, 0xd6757324,
- 0x12c07647, 0xc01f22c7, 0xbc42d2f3, 0x7555114a,
- 0x6efcd876, 0xd2df5480, 0x7c5df034, 0xc917c3b9,
- 0x6ea30afc, 0xbc238cf6, 0xae82a4b4, 0xb54a338d,
- 0x90884413, 0xd280860a, 0x12a12842, 0x1bc89739,
- 0x7c1f13f7, 0x4208c219, 0xc021ae48, 0x969bf7b,
- 0xcc7505eb, 0x3e17d1ee, 0x82296c51, 0xc9481133,
- 0x2b3708a7, 0xf262d405, 0xbc3ebdbf, 0x4b617d62,
- 0x2b7e1516, 0x28aed2a6, 0xabf71588, 0x9cf4f3c,
+ const exp_dec = [_]*const [32:0]u8{
+ "2b7e151628aed2a6abf7158809cf4f3c", "a0fafe1788542cb123a339392a6c7605", "f2c295f27a96b9435935807a7359f67f", "3d80477d4716fe3e1e237e446d7a883b", "ef44a541a8525b7fb671253bdb0bad00", "d4d1c6f87c839d87caf2b8bc11f915bc", "6d88a37a110b3efddbf98641ca0093fd", "4e54f70e5f5fc9f384a64fb24ea6dc4f", "ead27321b58dbad2312bf5607f8d292f", "ac7766f319fadc2128d12941575c006e", "d014f9a8c9ee2589e13f0cc8b6630ca6",
};
- var enc: [exp_enc.len]u32 = undefined;
- var dec: [exp_dec.len]u32 = undefined;
- expandKeyEncrypt(key[0..], enc[0..]);
- expandKeyDecrypt(enc[0..], dec[0..]);
- testing.expectEqualSlices(u32, exp_enc[0..], enc[0..]);
- testing.expectEqualSlices(u32, exp_dec[0..], dec[0..]);
+ const enc = AES128.initEnc(key);
+ const dec = AES128.initDec(key);
+ var exp: [16]u8 = undefined;
+
+ for (enc.key_schedule.round_keys) |round_key, i| {
+ try std.fmt.hexToBytes(&exp, exp_enc[i]);
+ testing.expectEqualSlices(u8, &exp, &round_key.toBytes());
+ }
+ for (enc.key_schedule.round_keys) |round_key, i| {
+ try std.fmt.hexToBytes(&exp, exp_dec[i]);
+ testing.expectEqualSlices(u8, &exp, &round_key.toBytes());
+ }
}
-// constants
+test "expand 256-bit key" {
+ const key = [_]u8{ 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4 };
+ const exp_enc = [_]*const [32:0]u8{
+ "603deb1015ca71be2b73aef0857d7781", "1f352c073b6108d72d9810a30914dff4", "9ba354118e6925afa51a8b5f2067fcde", "a8b09c1a93d194cdbe49846eb75d5b9a", "d59aecb85bf3c917fee94248de8ebe96", "b5a9328a2678a647983122292f6c79b3", "812c81addadf48ba24360af2fab8b464", "98c5bfc9bebd198e268c3ba709e04214", "68007bacb2df331696e939e46c518d80", "c814e20476a9fb8a5025c02d59c58239", "de1369676ccc5a71fa2563959674ee15", "5886ca5d2e2f31d77e0af1fa27cf73c3", "749c47ab18501ddae2757e4f7401905a", "cafaaae3e4d59b349adf6acebd10190d", "fe4890d1e6188d0b046df344706c631e",
+ };
+ const exp_dec = [_]*const [32:0]u8{
+ "fe4890d1e6188d0b046df344706c631e", "ada23f4963e23b2455427c8a5c709104", "57c96cf6074f07c0706abb07137f9241", "b668b621ce40046d36a047ae0932ed8e", "34ad1e4450866b367725bcc763152946", "32526c367828b24cf8e043c33f92aa20", "c440b289642b757227a3d7f114309581", "d669a7334a7ade7a80c8f18fc772e9e3", "25ba3c22a06bc7fb4388a28333934270", "54fb808b9c137949cab22ff547ba186c", "6c3d632985d1fbd9e3e36578701be0f3", "4a7459f9c8e8f9c256a156bc8d083799", "42107758e9ec98f066329ea193f8858b", "8ec6bff6829ca03b9e49af7edba96125", "603deb1015ca71be2b73aef0857d7781",
+ };
+ const enc = AES256.initEnc(key);
+ const dec = AES256.initDec(key);
+ var exp: [16]u8 = undefined;
-const poly = 1 << 8 | 1 << 4 | 1 << 3 | 1 << 1 | 1 << 0;
-
-const powx = [16]u8{
- 0x01,
- 0x02,
- 0x04,
- 0x08,
- 0x10,
- 0x20,
- 0x40,
- 0x80,
- 0x1b,
- 0x36,
- 0x6c,
- 0xd8,
- 0xab,
- 0x4d,
- 0x9a,
- 0x2f,
-};
-
-const sbox0 = [256]u8{
- 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
- 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
- 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
- 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
- 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
- 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
- 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
- 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
- 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
- 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
- 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
- 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
- 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
- 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
- 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
- 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
-};
-
-const sbox1 = [256]u8{
- 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
- 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
- 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
- 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
- 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
- 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
- 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
- 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
- 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
- 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
- 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
- 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
- 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
- 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
- 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
- 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,
-};
-
-const te0 = [256]u32{
- 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554,
- 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a,
- 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b,
- 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b,
- 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f,
- 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f,
- 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5,
- 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f,
- 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb,
- 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497,
- 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed,
- 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a,
- 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594,
- 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3,
- 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504,
- 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d,
- 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739,
- 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395,
- 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883,
- 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76,
- 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4,
- 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b,
- 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0,
- 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818,
- 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651,
- 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85,
- 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12,
- 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9,
- 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7,
- 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a,
- 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8,
- 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a,
-};
-const te1 = [256]u32{
- 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5,
- 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676,
- 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0,
- 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0,
- 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc,
- 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515,
- 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a,
- 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575,
- 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0,
- 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484,
- 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b,
- 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf,
- 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585,
- 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8,
- 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5,
- 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2,
- 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717,
- 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373,
- 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888,
- 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb,
- 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c,
- 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979,
- 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9,
- 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808,
- 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6,
- 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a,
- 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e,
- 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e,
- 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494,
- 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf,
- 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868,
- 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616,
-};
-const te2 = [256]u32{
- 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5,
- 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76,
- 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0,
- 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0,
- 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc,
- 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15,
- 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a,
- 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75,
- 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0,
- 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384,
- 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b,
- 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf,
- 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185,
- 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8,
- 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5,
- 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2,
- 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17,
- 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673,
- 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88,
- 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb,
- 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c,
- 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279,
- 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9,
- 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008,
- 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6,
- 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a,
- 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e,
- 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e,
- 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394,
- 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df,
- 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068,
- 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16,
-};
-const te3 = [256]u32{
- 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491,
- 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec,
- 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb,
- 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b,
- 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83,
- 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a,
- 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f,
- 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea,
- 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b,
- 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713,
- 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6,
- 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85,
- 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411,
- 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b,
- 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1,
- 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf,
- 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e,
- 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6,
- 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b,
- 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad,
- 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8,
- 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2,
- 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049,
- 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810,
- 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197,
- 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f,
- 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c,
- 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927,
- 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733,
- 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5,
- 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0,
- 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c,
-};
-
-const td0 = [256]u32{
- 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393,
- 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f,
- 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6,
- 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844,
- 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4,
- 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94,
- 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a,
- 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c,
- 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a,
- 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051,
- 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff,
- 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb,
- 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e,
- 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a,
- 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16,
- 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8,
- 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34,
- 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120,
- 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0,
- 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef,
- 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4,
- 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5,
- 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b,
- 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6,
- 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0,
- 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f,
- 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f,
- 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713,
- 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c,
- 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86,
- 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541,
- 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742,
-};
-const td1 = [256]u32{
- 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303,
- 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3,
- 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9,
- 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8,
- 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a,
- 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b,
- 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab,
- 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682,
- 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe,
- 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10,
- 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015,
- 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee,
- 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72,
- 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e,
- 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a,
- 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9,
- 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e,
- 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611,
- 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3,
- 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390,
- 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf,
- 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af,
- 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb,
- 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8,
- 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266,
- 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6,
- 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551,
- 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647,
- 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1,
- 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db,
- 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95,
- 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857,
-};
-const td2 = [256]u32{
- 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3,
- 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562,
- 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3,
- 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9,
- 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce,
- 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908,
- 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655,
- 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16,
- 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6,
- 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e,
- 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050,
- 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8,
- 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a,
- 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436,
- 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12,
- 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e,
- 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb,
- 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6,
- 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1,
- 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233,
- 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad,
- 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3,
- 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b,
- 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15,
- 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2,
- 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791,
- 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665,
- 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6,
- 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47,
- 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844,
- 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d,
- 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8,
-};
-const td3 = [256]u32{
- 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b,
- 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5,
- 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b,
- 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e,
- 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d,
- 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9,
- 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66,
- 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced,
- 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4,
- 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd,
- 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60,
- 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79,
- 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c,
- 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24,
- 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c,
- 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814,
- 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b,
- 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084,
- 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077,
- 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22,
- 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f,
- 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582,
- 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb,
- 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef,
- 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035,
- 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17,
- 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46,
- 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d,
- 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a,
- 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678,
- 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff,
- 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0,
-};
+ for (enc.key_schedule.round_keys) |round_key, i| {
+ try std.fmt.hexToBytes(&exp, exp_enc[i]);
+ testing.expectEqualSlices(u8, &exp, &round_key.toBytes());
+ }
+ for (dec.key_schedule.round_keys) |round_key, i| {
+ try std.fmt.hexToBytes(&exp, exp_dec[i]);
+ testing.expectEqualSlices(u8, &exp, &round_key.toBytes());
+ }
+}
diff --git a/lib/std/crypto/aes/aesni.zig b/lib/std/crypto/aes/aesni.zig
new file mode 100644
index 0000000000..47dd029bec
--- /dev/null
+++ b/lib/std/crypto/aes/aesni.zig
@@ -0,0 +1,430 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2020 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.
+// Based on Go stdlib implementation
+
+const std = @import("../../std.zig");
+const mem = std.mem;
+const debug = std.debug;
+const Vector = std.meta.Vector;
+
+const BlockVec = Vector(2, u64);
+
+/// A single AES block.
+pub const Block = struct {
+ pub const block_size: usize = 16;
+
+ /// Internal representation of a block.
+ repr: BlockVec,
+
+ /// Convert a byte sequence into an internal representation.
+ pub inline fn fromBytes(bytes: *const [16]u8) Block {
+ const repr = mem.bytesToValue(BlockVec, bytes);
+ return Block{ .repr = repr };
+ }
+
+ /// Convert the internal representation of a block into a byte sequence.
+ pub inline fn toBytes(block: Block) [16]u8 {
+ return mem.toBytes(block.repr);
+ }
+
+ /// XOR the block with a byte sequence.
+ pub inline fn xorBytes(block: Block, bytes: *const [16]u8) [16]u8 {
+ const x = block.repr ^ fromBytes(bytes).repr;
+ return mem.toBytes(x);
+ }
+
+ /// Encrypt a block with a round key.
+ pub inline fn encrypt(block: Block, round_key: Block) Block {
+ return Block{
+ .repr = asm (
+ \\ vaesenc %[rk], %[in], %[out]
+ : [out] "=x" (-> BlockVec)
+ : [in] "x" (block.repr),
+ [rk] "x" (round_key.repr)
+ ),
+ };
+ }
+
+ /// Encrypt a block with the last round key.
+ pub inline fn encryptLast(block: Block, round_key: Block) Block {
+ return Block{
+ .repr = asm (
+ \\ vaesenclast %[rk], %[in], %[out]
+ : [out] "=x" (-> BlockVec)
+ : [in] "x" (block.repr),
+ [rk] "x" (round_key.repr)
+ ),
+ };
+ }
+
+ /// Decrypt a block with a round key.
+ pub inline fn decrypt(block: Block, inv_round_key: Block) Block {
+ return Block{
+ .repr = asm (
+ \\ vaesdec %[rk], %[in], %[out]
+ : [out] "=x" (-> BlockVec)
+ : [in] "x" (block.repr),
+ [rk] "x" (inv_round_key.repr)
+ ),
+ };
+ }
+
+ /// Decrypt a block with the last round key.
+ pub inline fn decryptLast(block: Block, inv_round_key: Block) Block {
+ return Block{
+ .repr = asm (
+ \\ vaesdeclast %[rk], %[in], %[out]
+ : [out] "=x" (-> BlockVec)
+ : [in] "x" (block.repr),
+ [rk] "x" (inv_round_key.repr)
+ ),
+ };
+ }
+
+ /// Apply the bitwise XOR operation to the content of two blocks.
+ pub inline fn xorBlocks(block1: Block, block2: Block) Block {
+ return Block{ .repr = block1.repr ^ block2.repr };
+ }
+
+ /// Apply the bitwise AND operation to the content of two blocks.
+ pub inline fn andBlocks(block1: Block, block2: Block) Block {
+ return Block{ .repr = block1.repr & block2.repr };
+ }
+
+ /// Apply the bitwise OR operation to the content of two blocks.
+ pub inline fn orBlocks(block1: Block, block2: Block) Block {
+ return Block{ .repr = block1.repr | block2.repr };
+ }
+
+ /// Perform operations on multiple blocks in parallel.
+ pub const parallel = struct {
+ /// The recommended number of AES encryption/decryption to perform in parallel for the chosen implementation.
+ pub const optimal_parallel_blocks = 8;
+
+ /// Encrypt multiple blocks in parallel, each their own round key.
+ pub inline fn encryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) [count]Block {
+ comptime var i = 0;
+ var out: [count]Block = undefined;
+ inline while (i < count) : (i += 1) {
+ out[i] = blocks[i].encrypt(round_keys[i]);
+ }
+ return out;
+ }
+
+ /// Decrypt multiple blocks in parallel, each their own round key.
+ pub inline fn decryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) [count]Block {
+ comptime var i = 0;
+ var out: [count]Block = undefined;
+ inline while (i < count) : (i += 1) {
+ out[i] = blocks[i].decrypt(round_keys[i]);
+ }
+ return out;
+ }
+
+ /// Encrypt multple blocks in parallel with the same round key.
+ pub inline fn encryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block {
+ comptime var i = 0;
+ var out: [count]Block = undefined;
+ inline while (i < count) : (i += 1) {
+ out[i] = blocks[i].encrypt(round_key);
+ }
+ return out;
+ }
+
+ /// Decrypt multple blocks in parallel with the same round key.
+ pub inline fn decryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block {
+ comptime var i = 0;
+ var out: [count]Block = undefined;
+ inline while (i < count) : (i += 1) {
+ out[i] = blocks[i].decrypt(round_key);
+ }
+ return out;
+ }
+
+ /// Encrypt multple blocks in parallel with the same last round key.
+ pub inline fn encryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block {
+ comptime var i = 0;
+ var out: [count]Block = undefined;
+ inline while (i < count) : (i += 1) {
+ out[i] = blocks[i].encryptLast(round_key);
+ }
+ return out;
+ }
+
+ /// Decrypt multple blocks in parallel with the same last round key.
+ pub inline fn decryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block {
+ comptime var i = 0;
+ var out: [count]Block = undefined;
+ inline while (i < count) : (i += 1) {
+ out[i] = blocks[i].decryptLast(round_key);
+ }
+ return out;
+ }
+ };
+};
+
+fn KeySchedule(comptime AES: type) type {
+ std.debug.assert(AES.rounds == 10 or AES.rounds == 14);
+ const rounds = AES.rounds;
+
+ return struct {
+ const Self = @This();
+ round_keys: [rounds + 1]Block,
+
+ fn drc(comptime second: bool, comptime rc: u8, t: BlockVec, tx: BlockVec) BlockVec {
+ var s: BlockVec = undefined;
+ var ts: BlockVec = undefined;
+ return asm (
+ \\ vaeskeygenassist %[rc], %[t], %[s]
+ \\ vpslldq $4, %[tx], %[ts]
+ \\ vpxor %[ts], %[tx], %[r]
+ \\ vpslldq $8, %[r], %[ts]
+ \\ vpxor %[ts], %[r], %[r]
+ \\ vpshufd %[mask], %[s], %[ts]
+ \\ vpxor %[ts], %[r], %[r]
+ : [r] "=&x" (-> BlockVec),
+ [s] "=&x" (s),
+ [ts] "=&x" (ts)
+ : [rc] "n" (rc),
+ [t] "x" (t),
+ [tx] "x" (tx),
+ [mask] "n" (@as(u8, if (second) 0xaa else 0xff))
+ );
+ }
+
+ fn expand128(t1: *Block) Self {
+ var round_keys: [11]Block = undefined;
+ const rcs = [_]u8{ 1, 2, 4, 8, 16, 32, 64, 128, 27, 54 };
+ inline for (rcs) |rc, round| {
+ round_keys[round] = t1.*;
+ t1.repr = drc(false, rc, t1.repr, t1.repr);
+ }
+ round_keys[rcs.len] = t1.*;
+ return Self{ .round_keys = round_keys };
+ }
+
+ fn expand256(t1: *Block, t2: *Block) Self {
+ var round_keys: [15]Block = undefined;
+ const rcs = [_]u8{ 1, 2, 4, 8, 16, 32 };
+ round_keys[0] = t1.*;
+ inline for (rcs) |rc, round| {
+ round_keys[round * 2 + 1] = t2.*;
+ t1.repr = drc(false, rc, t2.repr, t1.repr);
+ round_keys[round * 2 + 2] = t1.*;
+ t2.repr = drc(true, rc, t1.repr, t2.repr);
+ }
+ round_keys[rcs.len * 2 + 1] = t2.*;
+ t1.repr = drc(false, 64, t2.repr, t1.repr);
+ round_keys[rcs.len * 2 + 2] = t1.*;
+ return Self{ .round_keys = round_keys };
+ }
+
+ /// Invert the key schedule.
+ pub fn invert(key_schedule: Self) Self {
+ const round_keys = &key_schedule.round_keys;
+ var inv_round_keys: [rounds + 1]Block = undefined;
+ inv_round_keys[0] = round_keys[rounds];
+ comptime var i = 1;
+ inline while (i < rounds) : (i += 1) {
+ inv_round_keys[i] = Block{
+ .repr = asm (
+ \\ vaesimc %[rk], %[inv_rk]
+ : [inv_rk] "=x" (-> BlockVec)
+ : [rk] "x" (round_keys[rounds - i].repr)
+ ),
+ };
+ }
+ inv_round_keys[rounds] = round_keys[0];
+ return Self{ .round_keys = inv_round_keys };
+ }
+ };
+}
+
+/// A context to perform encryption using the standard AES key schedule.
+pub fn AESEncryptCtx(comptime AES: type) type {
+ std.debug.assert(AES.key_bits == 128 or AES.key_bits == 256);
+ const rounds = AES.rounds;
+
+ return struct {
+ const Self = @This();
+ pub const block = AES.block;
+ pub const block_size = block.block_size;
+ key_schedule: KeySchedule(AES),
+
+ /// Create a new encryption context with the given key.
+ pub fn init(key: [AES.key_bits / 8]u8) Self {
+ var t1 = Block.fromBytes(key[0..16]);
+ const key_schedule = if (AES.key_bits == 128) ks: {
+ break :ks KeySchedule(AES).expand128(&t1);
+ } else ks: {
+ var t2 = Block.fromBytes(key[16..32]);
+ break :ks KeySchedule(AES).expand256(&t1, &t2);
+ };
+ return Self{
+ .key_schedule = key_schedule,
+ };
+ }
+
+ /// Encrypt a single block.
+ pub fn encrypt(ctx: Self, dst: *[16]u8, src: *const [16]u8) void {
+ const round_keys = ctx.key_schedule.round_keys;
+ var t = Block.fromBytes(src).xorBlocks(round_keys[0]);
+ comptime var i = 1;
+ inline while (i < rounds) : (i += 1) {
+ t = t.encrypt(round_keys[i]);
+ }
+ t = t.encryptLast(round_keys[rounds]);
+ dst.* = t.toBytes();
+ }
+
+ /// Encrypt+XOR a single block.
+ pub fn xor(ctx: Self, dst: *[16]u8, src: *const [16]u8, counter: [16]u8) void {
+ const round_keys = ctx.key_schedule.round_keys;
+ var t = Block.fromBytes(&counter).xorBlocks(round_keys[0]);
+ comptime var i = 1;
+ inline while (i < rounds) : (i += 1) {
+ t = t.encrypt(round_keys[i]);
+ }
+ t = t.encryptLast(round_keys[rounds]);
+ dst.* = t.xorBytes(src);
+ }
+
+ /// Encrypt multiple blocks, possibly leveraging parallelization.
+ pub fn encryptWide(ctx: Self, comptime count: usize, dst: *[16 * count]u8, src: *const [16 * count]u8) void {
+ const round_keys = ctx.key_schedule.round_keys;
+ var ts: [count]Block = undefined;
+ comptime var j = 0;
+ inline while (j < count) : (j += 1) {
+ ts[j] = Block.fromBytes(src[j * 16 .. j * 16 + 16][0..16]).xorBlocks(round_keys[0]);
+ }
+ comptime var i = 1;
+ inline while (i < rounds) : (i += 1) {
+ ts = Block.parallel.encryptWide(count, ts, round_keys[i]);
+ }
+ i = 1;
+ inline while (i < count) : (i += 1) {
+ ts = Block.parallel.encryptLastWide(count, ts, round_keys[i]);
+ }
+ j = 0;
+ inline while (j < count) : (j += 1) {
+ dst[16 * j .. 16 * j + 16].* = ts[j].toBytes();
+ }
+ }
+
+ /// Encrypt+XOR multiple blocks, possibly leveraging parallelization.
+ pub fn xorWide(ctx: Self, comptime count: usize, dst: *[16 * count]u8, src: *const [16 * count]u8, counters: [16 * count]u8) void {
+ const round_keys = ctx.key_schedule.round_keys;
+ var ts: [count]Block = undefined;
+ comptime var j = 0;
+ inline while (j < count) : (j += 1) {
+ ts[j] = Block.fromBytes(counters[j * 16 .. j * 16 + 16][0..16]).xorBlocks(round_keys[0]);
+ }
+ comptime var i = 1;
+ inline while (i < rounds) : (i += 1) {
+ ts = Block.parallel.encryptWide(count, ts, round_keys[i]);
+ }
+ ts = Block.parallel.encryptLastWide(count, ts, round_keys[i]);
+ j = 0;
+ inline while (j < count) : (j += 1) {
+ dst[16 * j .. 16 * j + 16].* = ts[j].xorBytes(src[16 * j .. 16 * j + 16]);
+ }
+ }
+ };
+}
+
+/// A context to perform decryption using the standard AES key schedule.
+pub fn AESDecryptCtx(comptime AES: type) type {
+ std.debug.assert(AES.key_bits == 128 or AES.key_bits == 256);
+ const rounds = AES.rounds;
+
+ return struct {
+ const Self = @This();
+ pub const block = AES.block;
+ pub const block_size = block.block_size;
+ key_schedule: KeySchedule(AES),
+
+ /// Create a decryption context from an existing encryption context.
+ pub fn initFromEnc(ctx: AESEncryptCtx(AES)) Self {
+ return Self{
+ .key_schedule = ctx.key_schedule.invert(),
+ };
+ }
+
+ /// Create a new decryption context with the given key.
+ pub fn init(key: [AES.key_bits / 8]u8) Self {
+ const enc_ctx = AESEncryptCtx(AES).init(key);
+ return initFromEnc(enc_ctx);
+ }
+
+ /// Decrypt a single block.
+ pub fn decrypt(ctx: Self, dst: *[16]u8, src: *const [16]u8) void {
+ const inv_round_keys = ctx.key_schedule.round_keys;
+ var t = Block.fromBytes(src).xorBlocks(inv_round_keys[0]);
+ comptime var i = 1;
+ inline while (i < rounds) : (i += 1) {
+ t = t.decrypt(inv_round_keys[i]);
+ }
+ t = t.decryptLast(inv_round_keys[rounds]);
+ dst.* = t.toBytes();
+ }
+
+ /// Decrypt multiple blocks, possibly leveraging parallelization.
+ pub fn decryptWide(ctx: Self, comptime count: usize, dst: *[16 * count]u8, src: *const [16 * count]u8) void {
+ const inv_round_keys = ctx.key_schedule.round_keys;
+ var ts: [count]Block = undefined;
+ comptime var j = 0;
+ inline while (j < count) : (j += 1) {
+ ts[j] = Block.fromBytes(src[j * 16 .. j * 16 + 16][0..16]).xorBlocks(inv_round_keys[0]);
+ }
+ comptime var i = 1;
+ inline while (i < rounds) : (i += 1) {
+ ts = Block.parallel.decryptWide(count, ts, inv_round_keys[i]);
+ }
+ i = 1;
+ inline while (i < count) : (i += 1) {
+ ts = Block.parallel.decryptLastWide(count, ts, inv_round_keys[i]);
+ }
+ j = 0;
+ inline while (j < count) : (j += 1) {
+ dst[16 * j .. 16 * j + 16].* = ts[j].toBytes();
+ }
+ }
+ };
+}
+
+/// AES-128 with the standard key schedule.
+pub const AES128 = struct {
+ pub const key_bits: usize = 128;
+ pub const rounds = ((key_bits - 64) / 32 + 8);
+ pub const block = Block;
+
+ /// Create a new context for encryption.
+ pub fn initEnc(key: [key_bits / 8]u8) AESEncryptCtx(AES128) {
+ return AESEncryptCtx(AES128).init(key);
+ }
+
+ /// Create a new context for decryption.
+ pub fn initDec(key: [key_bits / 8]u8) AESDecryptCtx(AES128) {
+ return AESDecryptCtx(AES128).init(key);
+ }
+};
+
+/// AES-256 with the standard key schedule.
+pub const AES256 = struct {
+ pub const key_bits: usize = 256;
+ pub const rounds = ((key_bits - 64) / 32 + 8);
+ pub const block = Block;
+
+ /// Create a new context for encryption.
+ pub fn initEnc(key: [key_bits / 8]u8) AESEncryptCtx(AES256) {
+ return AESEncryptCtx(AES256).init(key);
+ }
+
+ /// Create a new context for decryption.
+ pub fn initDec(key: [key_bits / 8]u8) AESDecryptCtx(AES256) {
+ return AESDecryptCtx(AES256).init(key);
+ }
+};
diff --git a/lib/std/crypto/aes/soft.zig b/lib/std/crypto/aes/soft.zig
new file mode 100644
index 0000000000..5f66f3499e
--- /dev/null
+++ b/lib/std/crypto/aes/soft.zig
@@ -0,0 +1,755 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2020 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.
+// Based on Go stdlib implementation
+
+const std = @import("../../std.zig");
+const mem = std.mem;
+
+const BlockVec = [4]u32;
+
+/// A single AES block.
+pub const Block = struct {
+ pub const block_size: usize = 16;
+
+ /// Internal representation of a block.
+ repr: BlockVec align(16),
+
+ /// Convert a byte sequence into an internal representation.
+ pub inline fn fromBytes(bytes: *const [16]u8) Block {
+ const s0 = mem.readIntBig(u32, bytes[0..4]);
+ const s1 = mem.readIntBig(u32, bytes[4..8]);
+ const s2 = mem.readIntBig(u32, bytes[8..12]);
+ const s3 = mem.readIntBig(u32, bytes[12..16]);
+ return Block{ .repr = BlockVec{ s0, s1, s2, s3 } };
+ }
+
+ /// Convert the internal representation of a block into a byte sequence.
+ pub inline fn toBytes(block: Block) [16]u8 {
+ var bytes: [16]u8 = undefined;
+ mem.writeIntBig(u32, bytes[0..4], block.repr[0]);
+ mem.writeIntBig(u32, bytes[4..8], block.repr[1]);
+ mem.writeIntBig(u32, bytes[8..12], block.repr[2]);
+ mem.writeIntBig(u32, bytes[12..16], block.repr[3]);
+ return bytes;
+ }
+
+ /// XOR the block with a byte sequence.
+ pub inline fn xorBytes(block: Block, bytes: *const [16]u8) [16]u8 {
+ const block_bytes = block.toBytes();
+ var x: [16]u8 = undefined;
+ comptime var i: usize = 0;
+ inline while (i < 16) : (i += 1) {
+ x[i] = block_bytes[i] ^ bytes[i];
+ }
+ return x;
+ }
+
+ /// Encrypt a block with a round key.
+ pub inline fn encrypt(block: Block, round_key: Block) Block {
+ const src = &block.repr;
+
+ const s0 = block.repr[0];
+ const s1 = block.repr[1];
+ const s2 = block.repr[2];
+ const s3 = block.repr[3];
+
+ const t0 = round_key.repr[0] ^ te0[@truncate(u8, s0 >> 24)] ^ te1[@truncate(u8, s1 >> 16)] ^ te2[@truncate(u8, s2 >> 8)] ^ te3[@truncate(u8, s3)];
+ const t1 = round_key.repr[1] ^ te0[@truncate(u8, s1 >> 24)] ^ te1[@truncate(u8, s2 >> 16)] ^ te2[@truncate(u8, s3 >> 8)] ^ te3[@truncate(u8, s0)];
+ const t2 = round_key.repr[2] ^ te0[@truncate(u8, s2 >> 24)] ^ te1[@truncate(u8, s3 >> 16)] ^ te2[@truncate(u8, s0 >> 8)] ^ te3[@truncate(u8, s1)];
+ const t3 = round_key.repr[3] ^ te0[@truncate(u8, s3 >> 24)] ^ te1[@truncate(u8, s0 >> 16)] ^ te2[@truncate(u8, s1 >> 8)] ^ te3[@truncate(u8, s2)];
+
+ return Block{ .repr = BlockVec{ t0, t1, t2, t3 } };
+ }
+
+ /// Encrypt a block with the last round key.
+ pub inline fn encryptLast(block: Block, round_key: Block) Block {
+ const src = &block.repr;
+
+ const t0 = block.repr[0];
+ const t1 = block.repr[1];
+ const t2 = block.repr[2];
+ const t3 = block.repr[3];
+
+ // Last round uses s-box directly and XORs to produce output.
+ var s0 = @as(u32, sbox0[t0 >> 24]) << 24 | @as(u32, sbox0[t1 >> 16 & 0xff]) << 16 | @as(u32, sbox0[t2 >> 8 & 0xff]) << 8 | @as(u32, sbox0[t3 & 0xff]);
+ var s1 = @as(u32, sbox0[t1 >> 24]) << 24 | @as(u32, sbox0[t2 >> 16 & 0xff]) << 16 | @as(u32, sbox0[t3 >> 8 & 0xff]) << 8 | @as(u32, sbox0[t0 & 0xff]);
+ var s2 = @as(u32, sbox0[t2 >> 24]) << 24 | @as(u32, sbox0[t3 >> 16 & 0xff]) << 16 | @as(u32, sbox0[t0 >> 8 & 0xff]) << 8 | @as(u32, sbox0[t1 & 0xff]);
+ var s3 = @as(u32, sbox0[t3 >> 24]) << 24 | @as(u32, sbox0[t0 >> 16 & 0xff]) << 16 | @as(u32, sbox0[t1 >> 8 & 0xff]) << 8 | @as(u32, sbox0[t2 & 0xff]);
+ s0 ^= round_key.repr[0];
+ s1 ^= round_key.repr[1];
+ s2 ^= round_key.repr[2];
+ s3 ^= round_key.repr[3];
+
+ return Block{ .repr = BlockVec{ s0, s1, s2, s3 } };
+ }
+
+ /// Decrypt a block with a round key.
+ pub inline fn decrypt(block: Block, round_key: Block) Block {
+ const src = &block.repr;
+
+ const s0 = block.repr[0];
+ const s1 = block.repr[1];
+ const s2 = block.repr[2];
+ const s3 = block.repr[3];
+
+ const t0 = round_key.repr[0] ^ td0[@truncate(u8, s0 >> 24)] ^ td1[@truncate(u8, s3 >> 16)] ^ td2[@truncate(u8, s2 >> 8)] ^ td3[@truncate(u8, s1)];
+ const t1 = round_key.repr[1] ^ td0[@truncate(u8, s1 >> 24)] ^ td1[@truncate(u8, s0 >> 16)] ^ td2[@truncate(u8, s3 >> 8)] ^ td3[@truncate(u8, s2)];
+ const t2 = round_key.repr[2] ^ td0[@truncate(u8, s2 >> 24)] ^ td1[@truncate(u8, s1 >> 16)] ^ td2[@truncate(u8, s0 >> 8)] ^ td3[@truncate(u8, s3)];
+ const t3 = round_key.repr[3] ^ td0[@truncate(u8, s3 >> 24)] ^ td1[@truncate(u8, s2 >> 16)] ^ td2[@truncate(u8, s1 >> 8)] ^ td3[@truncate(u8, s0)];
+
+ return Block{ .repr = BlockVec{ t0, t1, t2, t3 } };
+ }
+
+ /// Decrypt a block with the last round key.
+ pub inline fn decryptLast(block: Block, round_key: Block) Block {
+ const src = &block.repr;
+
+ const t0 = block.repr[0];
+ const t1 = block.repr[1];
+ const t2 = block.repr[2];
+ const t3 = block.repr[3];
+
+ // Last round uses s-box directly and XORs to produce output.
+ var s0 = @as(u32, sbox1[t0 >> 24]) << 24 | @as(u32, sbox1[t3 >> 16 & 0xff]) << 16 | @as(u32, sbox1[t2 >> 8 & 0xff]) << 8 | @as(u32, sbox1[t1 & 0xff]);
+ var s1 = @as(u32, sbox1[t1 >> 24]) << 24 | @as(u32, sbox1[t0 >> 16 & 0xff]) << 16 | @as(u32, sbox1[t3 >> 8 & 0xff]) << 8 | @as(u32, sbox1[t2 & 0xff]);
+ var s2 = @as(u32, sbox1[t2 >> 24]) << 24 | @as(u32, sbox1[t1 >> 16 & 0xff]) << 16 | @as(u32, sbox1[t0 >> 8 & 0xff]) << 8 | @as(u32, sbox1[t3 & 0xff]);
+ var s3 = @as(u32, sbox1[t3 >> 24]) << 24 | @as(u32, sbox1[t2 >> 16 & 0xff]) << 16 | @as(u32, sbox1[t1 >> 8 & 0xff]) << 8 | @as(u32, sbox1[t0 & 0xff]);
+ s0 ^= round_key.repr[0];
+ s1 ^= round_key.repr[1];
+ s2 ^= round_key.repr[2];
+ s3 ^= round_key.repr[3];
+
+ return Block{ .repr = BlockVec{ s0, s1, s2, s3 } };
+ }
+
+ /// Apply the bitwise XOR operation to the content of two blocks.
+ pub inline fn xorBlocks(block1: Block, block2: Block) Block {
+ var x: BlockVec = undefined;
+ comptime var i = 0;
+ inline while (i < 4) : (i += 1) {
+ x[i] = block1.repr[i] ^ block2.repr[i];
+ }
+ return Block{ .repr = x };
+ }
+
+ /// Apply the bitwise AND operation to the content of two blocks.
+ pub inline fn andBlocks(block1: Block, block2: Block) Block {
+ var x: BlockVec = undefined;
+ comptime var i = 0;
+ inline while (i < 4) : (i += 1) {
+ x[i] = block1.repr[i] & block2.repr[i];
+ }
+ return Block{ .repr = x };
+ }
+
+ /// Apply the bitwise OR operation to the content of two blocks.
+ pub inline fn orBlocks(block1: Block, block2: Block) Block {
+ var x: BlockVec = undefined;
+ comptime var i = 0;
+ inline while (i < 4) : (i += 1) {
+ x[i] = block1.repr[i] | block2.repr[i];
+ }
+ return Block{ .repr = x };
+ }
+
+ /// Perform operations on multiple blocks in parallel.
+ pub const parallel = struct {
+ /// The recommended number of AES encryption/decryption to perform in parallel for the chosen implementation.
+ pub const optimal_parallel_blocks = 1;
+
+ /// Encrypt multiple blocks in parallel, each their own round key.
+ pub fn encryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) [count]Block {
+ var i = 0;
+ var out: [count]Block = undefined;
+ while (i < count) : (i += 1) {
+ out[i] = blocks[i].encrypt(round_keys[i]);
+ }
+ return out;
+ }
+
+ /// Decrypt multiple blocks in parallel, each their own round key.
+ pub fn decryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) [count]Block {
+ var i = 0;
+ var out: [count]Block = undefined;
+ while (i < count) : (i += 1) {
+ out[i] = blocks[i].decrypt(round_keys[i]);
+ }
+ return out;
+ }
+
+ /// Encrypt multple blocks in parallel with the same round key.
+ pub fn encryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block {
+ var i = 0;
+ var out: [count]Block = undefined;
+ while (i < count) : (i += 1) {
+ out[i] = blocks[i].encrypt(round_key);
+ }
+ return out;
+ }
+
+ /// Decrypt multple blocks in parallel with the same round key.
+ pub fn decryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block {
+ var i = 0;
+ var out: [count]Block = undefined;
+ while (i < count) : (i += 1) {
+ out[i] = blocks[i].decrypt(round_key);
+ }
+ return out;
+ }
+
+ /// Encrypt multple blocks in parallel with the same last round key.
+ pub fn encryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block {
+ var i = 0;
+ var out: [count]Block = undefined;
+ while (i < count) : (i += 1) {
+ out[i] = blocks[i].encryptLast(round_key);
+ }
+ return out;
+ }
+
+ /// Decrypt multple blocks in parallel with the same last round key.
+ pub fn decryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block {
+ var i = 0;
+ var out: [count]Block = undefined;
+ while (i < count) : (i += 1) {
+ out[i] = blocks[i].decryptLast(round_key);
+ }
+ return out;
+ }
+ };
+};
+
+fn KeySchedule(comptime AES: type) type {
+ std.debug.assert(AES.rounds == 10 or AES.rounds == 14);
+ const key_size = AES.key_bits / 8;
+ const rounds = AES.rounds;
+
+ return struct {
+ const Self = @This();
+ const words_in_key = key_size / 4;
+
+ round_keys: [rounds + 1]Block,
+
+ // Key expansion algorithm. See FIPS-197, Figure 11.
+ fn expandKey(key: [key_size]u8) Self {
+ const subw = struct {
+ // Apply sbox0 to each byte in w.
+ fn func(w: u32) u32 {
+ return @as(u32, sbox0[w >> 24]) << 24 | @as(u32, sbox0[w >> 16 & 0xff]) << 16 | @as(u32, sbox0[w >> 8 & 0xff]) << 8 | @as(u32, sbox0[w & 0xff]);
+ }
+ }.func;
+
+ var round_keys: [rounds + 1]Block = undefined;
+ comptime var i: usize = 0;
+ inline while (i < words_in_key) : (i += 1) {
+ round_keys[i / 4].repr[i % 4] = mem.readIntBig(u32, key[4 * i ..][0..4]);
+ }
+ inline while (i < round_keys.len * 4) : (i += 1) {
+ var t = round_keys[(i - 1) / 4].repr[(i - 1) % 4];
+ if (i % words_in_key == 0) {
+ t = subw(std.math.rotl(u32, t, 8)) ^ (@as(u32, powx[i / words_in_key - 1]) << 24);
+ } else if (words_in_key > 6 and i % words_in_key == 4) {
+ t = subw(t);
+ }
+ round_keys[i / 4].repr[i % 4] = round_keys[(i - words_in_key) / 4].repr[(i - words_in_key) % 4] ^ t;
+ }
+ return Self{ .round_keys = round_keys };
+ }
+
+ /// Invert the key schedule.
+ pub fn invert(key_schedule: Self) Self {
+ const round_keys = &key_schedule.round_keys;
+ var inv_round_keys: [rounds + 1]Block = undefined;
+ const total_words = 4 * round_keys.len;
+ var i: usize = 0;
+ while (i < total_words) : (i += 4) {
+ const ei = total_words - i - 4;
+ comptime var j: usize = 0;
+ inline while (j < 4) : (j += 1) {
+ var x = round_keys[(ei + j) / 4].repr[(ei + j) % 4];
+ if (i > 0 and i + 4 < total_words) {
+ x = td0[sbox0[x >> 24]] ^ td1[sbox0[x >> 16 & 0xff]] ^ td2[sbox0[x >> 8 & 0xff]] ^ td3[sbox0[x & 0xff]];
+ }
+ inv_round_keys[(i + j) / 4].repr[(i + j) % 4] = x;
+ }
+ }
+ return Self{ .round_keys = inv_round_keys };
+ }
+ };
+}
+
+/// A context to perform encryption using the standard AES key schedule.
+pub fn AESEncryptCtx(comptime AES: type) type {
+ std.debug.assert(AES.key_bits == 128 or AES.key_bits == 256);
+ const rounds = AES.rounds;
+
+ return struct {
+ const Self = @This();
+ pub const block = AES.block;
+ pub const block_size = block.block_size;
+ key_schedule: KeySchedule(AES),
+
+ /// Create a new encryption context with the given key.
+ pub fn init(key: [AES.key_bits / 8]u8) Self {
+ const key_schedule = KeySchedule(AES).expandKey(key);
+ return Self{
+ .key_schedule = key_schedule,
+ };
+ }
+
+ /// Encrypt a single block.
+ pub fn encrypt(ctx: Self, dst: *[16]u8, src: *const [16]u8) void {
+ const round_keys = ctx.key_schedule.round_keys;
+ var t = Block.fromBytes(src).xorBlocks(round_keys[0]);
+ comptime var i = 1;
+ inline while (i < rounds) : (i += 1) {
+ t = t.encrypt(round_keys[i]);
+ }
+ t = t.encryptLast(round_keys[rounds]);
+ dst.* = t.toBytes();
+ }
+
+ /// Encrypt+XOR a single block.
+ pub fn xor(ctx: Self, dst: *[16]u8, src: *const [16]u8, counter: [16]u8) void {
+ const round_keys = ctx.key_schedule.round_keys;
+ var t = Block.fromBytes(&counter).xorBlocks(round_keys[0]);
+ comptime var i = 1;
+ inline while (i < rounds) : (i += 1) {
+ t = t.encrypt(round_keys[i]);
+ }
+ t = t.encryptLast(round_keys[rounds]);
+ dst.* = t.xorBytes(src);
+ }
+
+ /// Encrypt multiple blocks, possibly leveraging parallelization.
+ pub fn encryptWide(ctx: Self, comptime count: usize, dst: *[16 * count]u8, src: *const [16 * count]u8) void {
+ var i: usize = 0;
+ while (i < count) : (i += 1) {
+ ctx.encrypt(dst[16 * i .. 16 * i + 16][0..16], src[16 * i .. 16 * i + 16][0..16]);
+ }
+ }
+
+ /// Encrypt+XOR multiple blocks, possibly leveraging parallelization.
+ pub fn xorWide(ctx: Self, comptime count: usize, dst: *[16 * count]u8, src: *const [16 * count]u8, counters: [16 * count]u8) void {
+ var i: usize = 0;
+ while (i < count) : (i += 1) {
+ ctx.xor(dst[16 * i .. 16 * i + 16][0..16], src[16 * i .. 16 * i + 16][0..16], counters[16 * i .. 16 * i + 16][0..16].*);
+ }
+ }
+ };
+}
+
+/// A context to perform decryption using the standard AES key schedule.
+pub fn AESDecryptCtx(comptime AES: type) type {
+ std.debug.assert(AES.key_bits == 128 or AES.key_bits == 256);
+ const rounds = AES.rounds;
+
+ return struct {
+ const Self = @This();
+ pub const block = AES.block;
+ pub const block_size = block.block_size;
+ key_schedule: KeySchedule(AES),
+
+ /// Create a decryption context from an existing encryption context.
+ pub fn initFromEnc(ctx: AESEncryptCtx(AES)) Self {
+ return Self{
+ .key_schedule = ctx.key_schedule.invert(),
+ };
+ }
+
+ /// Create a new decryption context with the given key.
+ pub fn init(key: [AES.key_bits / 8]u8) Self {
+ const enc_ctx = AESEncryptCtx(AES).init(key);
+ return initFromEnc(enc_ctx);
+ }
+
+ /// Decrypt a single block.
+ pub fn decrypt(ctx: Self, dst: *[16]u8, src: *const [16]u8) void {
+ const inv_round_keys = ctx.key_schedule.round_keys;
+ var t = Block.fromBytes(src).xorBlocks(inv_round_keys[0]);
+ comptime var i = 1;
+ inline while (i < rounds) : (i += 1) {
+ t = t.decrypt(inv_round_keys[i]);
+ }
+ t = t.decryptLast(inv_round_keys[rounds]);
+ dst.* = t.toBytes();
+ }
+
+ /// Decrypt multiple blocks, possibly leveraging parallelization.
+ pub fn decryptWide(ctx: Self, comptime count: usize, dst: *[16 * count]u8, src: *const [16 * count]u8) void {
+ var i: usize = 0;
+ while (i < count) : (i += 1) {
+ ctx.decrypt(dst[16 * i .. 16 * i + 16][0..16], src[16 * i .. 16 * i + 16][0..16]);
+ }
+ }
+ };
+}
+
+/// AES-128 with the standard key schedule.
+pub const AES128 = struct {
+ pub const key_bits: usize = 128;
+ pub const rounds = ((key_bits - 64) / 32 + 8);
+ pub const block = Block;
+
+ /// Create a new context for encryption.
+ pub fn initEnc(key: [key_bits / 8]u8) AESEncryptCtx(AES128) {
+ return AESEncryptCtx(AES128).init(key);
+ }
+
+ /// Create a new context for decryption.
+ pub fn initDec(key: [key_bits / 8]u8) AESDecryptCtx(AES128) {
+ return AESDecryptCtx(AES128).init(key);
+ }
+};
+
+/// AES-256 with the standard key schedule.
+pub const AES256 = struct {
+ pub const key_bits: usize = 256;
+ pub const rounds = ((key_bits - 64) / 32 + 8);
+ pub const block = Block;
+
+ /// Create a new context for encryption.
+ pub fn initEnc(key: [key_bits / 8]u8) AESEncryptCtx(AES256) {
+ return AESEncryptCtx(AES256).init(key);
+ }
+
+ /// Create a new context for decryption.
+ pub fn initDec(key: [key_bits / 8]u8) AESDecryptCtx(AES256) {
+ return AESDecryptCtx(AES256).init(key);
+ }
+};
+
+// constants
+const powx = [16]u8{
+ 0x01,
+ 0x02,
+ 0x04,
+ 0x08,
+ 0x10,
+ 0x20,
+ 0x40,
+ 0x80,
+ 0x1b,
+ 0x36,
+ 0x6c,
+ 0xd8,
+ 0xab,
+ 0x4d,
+ 0x9a,
+ 0x2f,
+};
+
+const sbox0 align(64) = [256]u8{
+ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
+ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
+ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
+ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
+ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
+ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
+ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
+ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
+ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
+ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
+};
+
+const sbox1 align(64) = [256]u8{
+ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
+ 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
+ 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
+ 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
+ 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
+ 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
+ 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
+ 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
+ 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
+ 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
+ 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
+ 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
+ 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
+ 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
+ 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
+ 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,
+};
+
+const te0 align(64) = [256]u32{
+ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554,
+ 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a,
+ 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b,
+ 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b,
+ 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f,
+ 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f,
+ 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5,
+ 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f,
+ 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb,
+ 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497,
+ 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed,
+ 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a,
+ 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594,
+ 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3,
+ 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504,
+ 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d,
+ 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739,
+ 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395,
+ 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883,
+ 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76,
+ 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4,
+ 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b,
+ 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0,
+ 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818,
+ 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651,
+ 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85,
+ 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12,
+ 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9,
+ 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7,
+ 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a,
+ 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8,
+ 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a,
+};
+const te1 align(64) = [256]u32{
+ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5,
+ 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676,
+ 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0,
+ 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0,
+ 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc,
+ 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515,
+ 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a,
+ 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575,
+ 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0,
+ 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484,
+ 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b,
+ 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf,
+ 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585,
+ 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8,
+ 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5,
+ 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2,
+ 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717,
+ 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373,
+ 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888,
+ 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb,
+ 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c,
+ 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979,
+ 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9,
+ 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808,
+ 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6,
+ 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a,
+ 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e,
+ 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e,
+ 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494,
+ 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf,
+ 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868,
+ 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616,
+};
+const te2 align(64) = [256]u32{
+ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5,
+ 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76,
+ 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0,
+ 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0,
+ 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc,
+ 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15,
+ 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a,
+ 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75,
+ 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0,
+ 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384,
+ 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b,
+ 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf,
+ 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185,
+ 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8,
+ 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5,
+ 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2,
+ 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17,
+ 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673,
+ 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88,
+ 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb,
+ 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c,
+ 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279,
+ 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9,
+ 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008,
+ 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6,
+ 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a,
+ 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e,
+ 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e,
+ 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394,
+ 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df,
+ 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068,
+ 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16,
+};
+const te3 align(64) = [256]u32{
+ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491,
+ 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec,
+ 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb,
+ 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b,
+ 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83,
+ 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a,
+ 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f,
+ 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea,
+ 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b,
+ 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713,
+ 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6,
+ 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85,
+ 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411,
+ 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b,
+ 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1,
+ 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf,
+ 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e,
+ 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6,
+ 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b,
+ 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad,
+ 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8,
+ 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2,
+ 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049,
+ 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810,
+ 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197,
+ 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f,
+ 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c,
+ 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927,
+ 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733,
+ 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5,
+ 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0,
+ 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c,
+};
+
+const td0 align(64) = [256]u32{
+ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393,
+ 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f,
+ 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6,
+ 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844,
+ 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4,
+ 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94,
+ 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a,
+ 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c,
+ 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a,
+ 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051,
+ 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff,
+ 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb,
+ 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e,
+ 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a,
+ 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16,
+ 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8,
+ 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34,
+ 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120,
+ 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0,
+ 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef,
+ 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4,
+ 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5,
+ 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b,
+ 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6,
+ 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0,
+ 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f,
+ 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f,
+ 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713,
+ 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c,
+ 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86,
+ 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541,
+ 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742,
+};
+const td1 align(64) = [256]u32{
+ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303,
+ 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3,
+ 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9,
+ 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8,
+ 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a,
+ 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b,
+ 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab,
+ 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682,
+ 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe,
+ 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10,
+ 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015,
+ 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee,
+ 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72,
+ 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e,
+ 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a,
+ 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9,
+ 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e,
+ 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611,
+ 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3,
+ 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390,
+ 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf,
+ 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af,
+ 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb,
+ 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8,
+ 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266,
+ 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6,
+ 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551,
+ 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647,
+ 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1,
+ 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db,
+ 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95,
+ 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857,
+};
+const td2 align(64) = [256]u32{
+ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3,
+ 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562,
+ 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3,
+ 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9,
+ 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce,
+ 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908,
+ 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655,
+ 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16,
+ 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6,
+ 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e,
+ 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050,
+ 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8,
+ 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a,
+ 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436,
+ 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12,
+ 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e,
+ 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb,
+ 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6,
+ 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1,
+ 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233,
+ 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad,
+ 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3,
+ 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b,
+ 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15,
+ 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2,
+ 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791,
+ 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665,
+ 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6,
+ 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47,
+ 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844,
+ 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d,
+ 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8,
+};
+const td3 align(64) = [256]u32{
+ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b,
+ 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5,
+ 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b,
+ 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e,
+ 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d,
+ 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9,
+ 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66,
+ 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced,
+ 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4,
+ 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd,
+ 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60,
+ 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79,
+ 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c,
+ 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24,
+ 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c,
+ 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814,
+ 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b,
+ 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084,
+ 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077,
+ 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22,
+ 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f,
+ 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582,
+ 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb,
+ 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef,
+ 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035,
+ 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17,
+ 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46,
+ 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d,
+ 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a,
+ 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678,
+ 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff,
+ 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0,
+};
diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig
index e20b27220e..3c7e3445a2 100644
--- a/lib/std/crypto/benchmark.zig
+++ b/lib/std/crypto/benchmark.zig
@@ -149,6 +149,8 @@ const aeads = [_]Crypto{
Crypto{ .ty = crypto.aead.ChaCha20Poly1305, .name = "chacha20Poly1305" },
Crypto{ .ty = crypto.aead.XChaCha20Poly1305, .name = "xchacha20Poly1305" },
Crypto{ .ty = crypto.aead.Gimli, .name = "gimli-aead" },
+ Crypto{ .ty = crypto.aead.AEGIS128L, .name = "aegis-128l" },
+ Crypto{ .ty = crypto.aead.AEGIS256, .name = "aegis-256" },
};
pub fn benchmarkAead(comptime Aead: anytype, comptime bytes: comptime_int) !u64 {
@@ -168,7 +170,7 @@ pub fn benchmarkAead(comptime Aead: anytype, comptime bytes: comptime_int) !u64
const start = timer.lap();
while (offset < bytes) : (offset += in.len) {
Aead.encrypt(in[0..], tag[0..], in[0..], &[_]u8{}, nonce, key);
- Aead.decrypt(in[0..], in[0..], tag, &[_]u8{}, nonce, key) catch unreachable;
+ try Aead.decrypt(in[0..], in[0..], tag, &[_]u8{}, nonce, key);
}
mem.doNotOptimizeAway(&in);
const end = timer.read();
@@ -179,6 +181,64 @@ pub fn benchmarkAead(comptime Aead: anytype, comptime bytes: comptime_int) !u64
return throughput;
}
+const aes = [_]Crypto{
+ Crypto{ .ty = crypto.core.aes.AES128, .name = "aes128-single" },
+ Crypto{ .ty = crypto.core.aes.AES256, .name = "aes256-single" },
+};
+
+pub fn benchmarkAES(comptime AES: anytype, comptime count: comptime_int) !u64 {
+ var key: [AES.key_bits / 8]u8 = undefined;
+ prng.random.bytes(key[0..]);
+ const ctx = AES.initEnc(key);
+
+ var in = [_]u8{0} ** 16;
+
+ var timer = try Timer.start();
+ const start = timer.lap();
+ {
+ var i: usize = 0;
+ while (i < count) : (i += 1) {
+ ctx.encrypt(&in, &in);
+ }
+ }
+ mem.doNotOptimizeAway(&in);
+ const end = timer.read();
+
+ const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
+ const throughput = @floatToInt(u64, count / elapsed_s);
+
+ return throughput;
+}
+
+const aes8 = [_]Crypto{
+ Crypto{ .ty = crypto.core.aes.AES128, .name = "aes128-8" },
+ Crypto{ .ty = crypto.core.aes.AES256, .name = "aes256-8" },
+};
+
+pub fn benchmarkAES8(comptime AES: anytype, comptime count: comptime_int) !u64 {
+ var key: [AES.key_bits / 8]u8 = undefined;
+ prng.random.bytes(key[0..]);
+ const ctx = AES.initEnc(key);
+
+ var in = [_]u8{0} ** (8 * 16);
+
+ var timer = try Timer.start();
+ const start = timer.lap();
+ {
+ var i: usize = 0;
+ while (i < count) : (i += 1) {
+ ctx.encryptWide(8, &in, &in);
+ }
+ }
+ mem.doNotOptimizeAway(&in);
+ const end = timer.read();
+
+ const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
+ const throughput = @floatToInt(u64, 8 * count / elapsed_s);
+
+ return throughput;
+}
+
fn usage() void {
std.debug.warn(
\\throughput_test [options]
@@ -238,35 +298,49 @@ pub fn main() !void {
inline for (hashes) |H| {
if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {
const throughput = try benchmarkHash(H.ty, mode(128 * MiB));
- try stdout.print("{:>17}: {:7} MiB/s\n", .{ H.name, throughput / (1 * MiB) });
+ try stdout.print("{:>17}: {:10} MiB/s\n", .{ H.name, throughput / (1 * MiB) });
}
}
inline for (macs) |M| {
if (filter == null or std.mem.indexOf(u8, M.name, filter.?) != null) {
const throughput = try benchmarkMac(M.ty, mode(128 * MiB));
- try stdout.print("{:>17}: {:7} MiB/s\n", .{ M.name, throughput / (1 * MiB) });
+ try stdout.print("{:>17}: {:10} MiB/s\n", .{ M.name, throughput / (1 * MiB) });
}
}
inline for (exchanges) |E| {
if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
const throughput = try benchmarkKeyExchange(E.ty, mode(1000));
- try stdout.print("{:>17}: {:7} exchanges/s\n", .{ E.name, throughput });
+ try stdout.print("{:>17}: {:10} exchanges/s\n", .{ E.name, throughput });
}
}
inline for (signatures) |E| {
if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
const throughput = try benchmarkSignature(E.ty, mode(1000));
- try stdout.print("{:>17}: {:7} signatures/s\n", .{ E.name, throughput });
+ try stdout.print("{:>17}: {:10} signatures/s\n", .{ E.name, throughput });
}
}
inline for (aeads) |E| {
if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
const throughput = try benchmarkAead(E.ty, mode(128 * MiB));
- try stdout.print("{:>17}: {:7} MiB/s\n", .{ E.name, throughput / (1 * MiB) });
+ try stdout.print("{:>17}: {:10} MiB/s\n", .{ E.name, throughput / (1 * MiB) });
+ }
+ }
+
+ inline for (aes) |E| {
+ if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
+ const throughput = try benchmarkAES(E.ty, mode(100000000));
+ try stdout.print("{:>17}: {:10} ops/s\n", .{ E.name, throughput });
+ }
+ }
+
+ inline for (aes8) |E| {
+ if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
+ const throughput = try benchmarkAES8(E.ty, mode(10000000));
+ try stdout.print("{:>17}: {:10} ops/s\n", .{ E.name, throughput });
}
}
}
diff --git a/lib/std/crypto/gimli.zig b/lib/std/crypto/gimli.zig
index 5b572aad7d..10e8a7dff0 100644
--- a/lib/std/crypto/gimli.zig
+++ b/lib/std/crypto/gimli.zig
@@ -38,7 +38,35 @@ pub const State = struct {
return mem.sliceAsBytes(self.data[0..]);
}
- pub fn permute(self: *Self) void {
+ fn permute_unrolled(self: *Self) void {
+ const state = &self.data;
+ comptime var round = @as(u32, 24);
+ inline while (round > 0) : (round -= 1) {
+ var column = @as(usize, 0);
+ while (column < 4) : (column += 1) {
+ const x = math.rotl(u32, state[column], 24);
+ const y = math.rotl(u32, state[4 + column], 9);
+ const z = state[8 + column];
+ state[8 + column] = ((x ^ (z << 1)) ^ ((y & z) << 2));
+ state[4 + column] = ((y ^ x) ^ ((x | z) << 1));
+ state[column] = ((z ^ y) ^ ((x & y) << 3));
+ }
+ switch (round & 3) {
+ 0 => {
+ mem.swap(u32, &state[0], &state[1]);
+ mem.swap(u32, &state[2], &state[3]);
+ state[0] ^= round | 0x9e377900;
+ },
+ 2 => {
+ mem.swap(u32, &state[0], &state[2]);
+ mem.swap(u32, &state[1], &state[3]);
+ },
+ else => {},
+ }
+ }
+ }
+
+ fn permute_small(self: *Self) void {
const state = &self.data;
var round = @as(u32, 24);
while (round > 0) : (round -= 1) {
@@ -66,6 +94,8 @@ pub const State = struct {
}
}
+ pub const permute = if (std.builtin.mode == .ReleaseSmall) permute_small else permute_unrolled;
+
pub fn squeeze(self: *Self, out: []u8) void {
var i = @as(usize, 0);
while (i + RATE <= out.len) : (i += RATE) {
@@ -249,15 +279,15 @@ pub const Aead = struct {
in = in[State.RATE..];
out = out[State.RATE..];
}) {
- for (buf[0..State.RATE]) |*p, i| {
- p.* ^= in[i];
- out[i] = p.*;
+ for (in[0..State.RATE]) |v, i| {
+ buf[i] ^= v;
}
+ mem.copy(u8, out[0..State.RATE], buf[0..State.RATE]);
state.permute();
}
- for (buf[0..in.len]) |*p, i| {
- p.* ^= in[i];
- out[i] = p.*;
+ for (in[0..]) |v, i| {
+ buf[i] ^= v;
+ out[i] = buf[i];
}
// XOR 1 into the next byte of the state
@@ -291,15 +321,17 @@ pub const Aead = struct {
in = in[State.RATE..];
out = out[State.RATE..];
}) {
- for (buf[0..State.RATE]) |*p, i| {
- out[i] = p.* ^ in[i];
- p.* = in[i];
+ const d = in[0..State.RATE].*;
+ for (d) |v, i| {
+ out[i] = buf[i] ^ v;
}
+ mem.copy(u8, buf[0..State.RATE], d[0..State.RATE]);
state.permute();
}
for (buf[0..in.len]) |*p, i| {
- out[i] = p.* ^ in[i];
- p.* = in[i];
+ const d = in[i];
+ out[i] = p.* ^ d;
+ p.* = d;
}
// XOR 1 into the next byte of the state
diff --git a/lib/std/crypto/modes.zig b/lib/std/crypto/modes.zig
new file mode 100644
index 0000000000..5c1fa4b2f3
--- /dev/null
+++ b/lib/std/crypto/modes.zig
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2020 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.
+// Based on Go stdlib implementation
+
+const std = @import("../std.zig");
+const builtin = std.builtin;
+const mem = std.mem;
+const debug = std.debug;
+
+/// Counter mode.
+///
+/// This mode creates a key stream by encrypting an incrementing counter using a block cipher, and adding it to the source material.
+///
+/// Important: the counter mode doesn't provide authenticated encryption: the ciphertext can be trivially modified without this being detected.
+/// As a result, applications should generally never use it directly, but only in a construction that includes a MAC.
+pub fn ctr(comptime BlockCipher: anytype, block_cipher: BlockCipher, dst: []u8, src: []const u8, iv: [BlockCipher.block_size]u8, endian: comptime builtin.Endian) void {
+ debug.assert(dst.len >= src.len);
+ const block_size = BlockCipher.block_size;
+ var counter: [BlockCipher.block_size]u8 = undefined;
+ var counterInt = mem.readInt(u128, &iv, endian);
+ var i: usize = 0;
+
+ const parallel_count = BlockCipher.block.parallel.optimal_parallel_blocks;
+ const wide_block_size = parallel_count * 16;
+ if (src.len >= wide_block_size) {
+ var counters: [parallel_count * 16]u8 = undefined;
+ while (i + wide_block_size <= src.len) : (i += wide_block_size) {
+ comptime var j = 0;
+ inline while (j < parallel_count) : (j += 1) {
+ mem.writeInt(u128, counters[j * 16 .. j * 16 + 16], counterInt, endian);
+ counterInt +%= 1;
+ }
+ block_cipher.xorWide(parallel_count, dst[i .. i + wide_block_size][0..wide_block_size], src[i .. i + wide_block_size][0..wide_block_size], counters);
+ }
+ }
+ while (i + block_size <= src.len) : (i += block_size) {
+ mem.writeInt(u128, &counter, counterInt, endian);
+ counterInt +%= 1;
+ block_cipher.xor(dst[i .. i + block_size][0..block_size], src[i .. i + block_size][0..block_size], counter);
+ }
+ if (i < src.len) {
+ mem.writeInt(u128, &counter, counterInt, endian);
+ var pad = [_]u8{0} ** block_size;
+ mem.copy(u8, &pad, src[i..]);
+ block_cipher.xor(&pad, &pad, counter);
+ mem.copy(u8, dst[i..], pad[0 .. src.len - i]);
+ }
+}
diff --git a/lib/std/event/future.zig b/lib/std/event/future.zig
index c9777288e4..40c7845d53 100644
--- a/lib/std/event/future.zig
+++ b/lib/std/event/future.zig
@@ -95,7 +95,7 @@ test "std.event.Future" {
// TODO provide a way to run tests in evented I/O mode
if (!std.io.is_async) return error.SkipZigTest;
- const handle = async testFuture();
+ testFuture();
}
fn testFuture() void {
diff --git a/lib/std/event/lock.zig b/lib/std/event/lock.zig
index d27a12aef8..6819e413d2 100644
--- a/lib/std/event/lock.zig
+++ b/lib/std/event/lock.zig
@@ -16,107 +16,111 @@ const Loop = std.event.Loop;
/// Allows only one actor to hold the lock.
/// TODO: make this API also work in blocking I/O mode.
pub const Lock = struct {
- shared: bool,
- queue: Queue,
- queue_empty: bool,
+ mutex: std.Mutex = std.Mutex{},
+ head: usize = UNLOCKED,
- const Queue = std.atomic.Queue(anyframe);
+ const UNLOCKED = 0;
+ const LOCKED = 1;
const global_event_loop = Loop.instance orelse
@compileError("std.event.Lock currently only works with event-based I/O");
+ const Waiter = struct {
+ // forced Waiter alignment to ensure it doesn't clash with LOCKED
+ next: ?*Waiter align(2),
+ tail: *Waiter,
+ node: Loop.NextTickNode,
+ };
+
+ pub fn initLocked() Lock {
+ return Lock{ .head = LOCKED };
+ }
+
+ pub fn acquire(self: *Lock) Held {
+ const held = self.mutex.acquire();
+
+ // self.head transitions from multiple stages depending on the value:
+ // UNLOCKED -> LOCKED:
+ // acquire Lock ownership when theres no waiters
+ // LOCKED -> :
+ // Lock is already owned, enqueue first Waiter
+ // -> :
+ // Lock is owned with pending waiters. Push our waiter to the queue.
+
+ if (self.head == UNLOCKED) {
+ self.head = LOCKED;
+ held.release();
+ return Held{ .lock = self };
+ }
+
+ var waiter: Waiter = undefined;
+ waiter.next = null;
+ waiter.tail = &waiter;
+
+ const head = switch (self.head) {
+ UNLOCKED => unreachable,
+ LOCKED => null,
+ else => @intToPtr(*Waiter, self.head),
+ };
+
+ if (head) |h| {
+ h.tail.next = &waiter;
+ h.tail = &waiter;
+ } else {
+ self.head = @ptrToInt(&waiter);
+ }
+
+ suspend {
+ waiter.node = Loop.NextTickNode{
+ .prev = undefined,
+ .next = undefined,
+ .data = @frame(),
+ };
+ held.release();
+ }
+
+ return Held{ .lock = self };
+ }
+
pub const Held = struct {
lock: *Lock,
pub fn release(self: Held) void {
- // Resume the next item from the queue.
- if (self.lock.queue.get()) |node| {
- global_event_loop.onNextTick(node);
- return;
- }
+ const waiter = blk: {
+ const held = self.lock.mutex.acquire();
+ defer held.release();
- // We need to release the lock.
- @atomicStore(bool, &self.lock.queue_empty, true, .SeqCst);
- @atomicStore(bool, &self.lock.shared, false, .SeqCst);
+ // self.head goes through the reverse transition from acquire():
+ // -> :
+ // pop a waiter from the queue to give Lock ownership when theres still others pending
+ // -> LOCKED:
+ // pop the laster waiter from the queue, while also giving it lock ownership when awaken
+ // LOCKED -> UNLOCKED:
+ // last lock owner releases lock while no one else is waiting for it
- // There might be a queue item. If we know the queue is empty, we can be done,
- // because the other actor will try to obtain the lock.
- // But if there's a queue item, we are the actor which must loop and attempt
- // to grab the lock again.
- if (@atomicLoad(bool, &self.lock.queue_empty, .SeqCst)) {
- return;
- }
-
- while (true) {
- if (@atomicRmw(bool, &self.lock.shared, .Xchg, true, .SeqCst)) {
- // We did not obtain the lock. Great, the queue is someone else's problem.
- return;
+ switch (self.lock.head) {
+ UNLOCKED => {
+ unreachable; // Lock unlocked while unlocking
+ },
+ LOCKED => {
+ self.lock.head = UNLOCKED;
+ break :blk null;
+ },
+ else => {
+ const waiter = @intToPtr(*Waiter, self.lock.head);
+ self.lock.head = if (waiter.next == null) LOCKED else @ptrToInt(waiter.next);
+ if (waiter.next) |next|
+ next.tail = waiter.tail;
+ break :blk waiter;
+ },
}
+ };
- // Resume the next item from the queue.
- if (self.lock.queue.get()) |node| {
- global_event_loop.onNextTick(node);
- return;
- }
-
- // Release the lock again.
- @atomicStore(bool, &self.lock.queue_empty, true, .SeqCst);
- @atomicStore(bool, &self.lock.shared, false, .SeqCst);
-
- // Find out if we can be done.
- if (@atomicLoad(bool, &self.lock.queue_empty, .SeqCst)) {
- return;
- }
+ if (waiter) |w| {
+ global_event_loop.onNextTick(&w.node);
}
}
};
-
- pub fn init() Lock {
- return Lock{
- .shared = false,
- .queue = Queue.init(),
- .queue_empty = true,
- };
- }
-
- pub fn initLocked() Lock {
- return Lock{
- .shared = true,
- .queue = Queue.init(),
- .queue_empty = true,
- };
- }
-
- /// Must be called when not locked. Not thread safe.
- /// All calls to acquire() and release() must complete before calling deinit().
- pub fn deinit(self: *Lock) void {
- assert(!self.shared);
- while (self.queue.get()) |node| resume node.data;
- }
-
- pub fn acquire(self: *Lock) callconv(.Async) Held {
- var my_tick_node = Loop.NextTickNode.init(@frame());
-
- errdefer _ = self.queue.remove(&my_tick_node); // TODO test canceling an acquire
- suspend {
- self.queue.put(&my_tick_node);
-
- // At this point, we are in the queue, so we might have already been resumed.
-
- // We set this bit so that later we can rely on the fact, that if queue_empty == true, some actor
- // will attempt to grab the lock.
- @atomicStore(bool, &self.queue_empty, false, .SeqCst);
-
- if (!@atomicRmw(bool, &self.shared, .Xchg, true, .SeqCst)) {
- if (self.queue.get()) |node| {
- // Whether this node is us or someone else, we tail resume it.
- resume node.data;
- }
- }
- }
-
- return Held{ .lock = self };
- }
};
test "std.event.Lock" {
@@ -128,41 +132,16 @@ test "std.event.Lock" {
// TODO https://github.com/ziglang/zig/issues/3251
if (builtin.os.tag == .freebsd) return error.SkipZigTest;
- // TODO this file has bit-rotted. repair it
- if (true) return error.SkipZigTest;
-
- var lock = Lock.init();
- defer lock.deinit();
-
- _ = async testLock(&lock);
+ var lock = Lock{};
+ testLock(&lock);
const expected_result = [1]i32{3 * @intCast(i32, shared_test_data.len)} ** shared_test_data.len;
testing.expectEqualSlices(i32, &expected_result, &shared_test_data);
}
-fn testLock(lock: *Lock) callconv(.Async) void {
+fn testLock(lock: *Lock) void {
var handle1 = async lockRunner(lock);
- var tick_node1 = Loop.NextTickNode{
- .prev = undefined,
- .next = undefined,
- .data = &handle1,
- };
- Loop.instance.?.onNextTick(&tick_node1);
-
var handle2 = async lockRunner(lock);
- var tick_node2 = Loop.NextTickNode{
- .prev = undefined,
- .next = undefined,
- .data = &handle2,
- };
- Loop.instance.?.onNextTick(&tick_node2);
-
var handle3 = async lockRunner(lock);
- var tick_node3 = Loop.NextTickNode{
- .prev = undefined,
- .next = undefined,
- .data = &handle3,
- };
- Loop.instance.?.onNextTick(&tick_node3);
await handle1;
await handle2;
@@ -171,13 +150,13 @@ fn testLock(lock: *Lock) callconv(.Async) void {
var shared_test_data = [1]i32{0} ** 10;
var shared_test_index: usize = 0;
-fn lockRunner(lock: *Lock) callconv(.Async) void {
- suspend; // resumed by onNextTick
+
+fn lockRunner(lock: *Lock) void {
+ Lock.global_event_loop.yield();
var i: usize = 0;
while (i < shared_test_data.len) : (i += 1) {
- var lock_frame = async lock.acquire();
- const handle = await lock_frame;
+ const handle = lock.acquire();
defer handle.release();
shared_test_index = 0;
diff --git a/lib/std/event/loop.zig b/lib/std/event/loop.zig
index 2600b337b3..2ed9f938d8 100644
--- a/lib/std/event/loop.zig
+++ b/lib/std/event/loop.zig
@@ -112,8 +112,9 @@ pub const Loop = struct {
/// have the correct pointer value.
/// https://github.com/ziglang/zig/issues/2761 and https://github.com/ziglang/zig/issues/2765
pub fn init(self: *Loop) !void {
- if (builtin.single_threaded
- or (@hasDecl(root, "event_loop_mode") and root.event_loop_mode == .single_threaded)) {
+ if (builtin.single_threaded or
+ (@hasDecl(root, "event_loop_mode") and root.event_loop_mode == .single_threaded))
+ {
return self.initSingleThreaded();
} else {
return self.initMultiThreaded();
@@ -687,9 +688,14 @@ pub const Loop = struct {
switch (builtin.os.tag) {
.linux => {
- // writing 8 bytes to an eventfd cannot fail
- const amt = os.write(self.os_data.final_eventfd, &wakeup_bytes) catch unreachable;
- assert(amt == wakeup_bytes.len);
+ // writing to the eventfd will only wake up one thread, thus multiple writes
+ // are needed to wakeup all the threads
+ var i: usize = 0;
+ while (i < self.extra_threads.len + 1) : (i += 1) {
+ // writing 8 bytes to an eventfd cannot fail
+ const amt = os.write(self.os_data.final_eventfd, &wakeup_bytes) catch unreachable;
+ assert(amt == wakeup_bytes.len);
+ }
return;
},
.macosx, .freebsd, .netbsd, .dragonfly => {
@@ -715,6 +721,50 @@ pub const Loop = struct {
}
}
+ /// ------- I/0 APIs -------
+ pub fn accept(
+ self: *Loop,
+ /// This argument is a socket that has been created with `socket`, bound to a local address
+ /// with `bind`, and is listening for connections after a `listen`.
+ sockfd: os.fd_t,
+ /// This argument is a pointer to a sockaddr structure. This structure is filled in with the
+ /// address of the peer socket, as known to the communications layer. The exact format of the
+ /// address returned addr is determined by the socket's address family (see `socket` and the
+ /// respective protocol man pages).
+ addr: *os.sockaddr,
+ /// This argument is a value-result argument: the caller must initialize it to contain the
+ /// size (in bytes) of the structure pointed to by addr; on return it will contain the actual size
+ /// of the peer address.
+ ///
+ /// The returned address is truncated if the buffer provided is too small; in this case, `addr_size`
+ /// will return a value greater than was supplied to the call.
+ addr_size: *os.socklen_t,
+ /// The following values can be bitwise ORed in flags to obtain different behavior:
+ /// * `SOCK_CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. See the
+ /// description of the `O_CLOEXEC` flag in `open` for reasons why this may be useful.
+ flags: u32,
+ ) os.AcceptError!os.fd_t {
+ while (true) {
+ return os.accept(sockfd, addr, addr_size, flags | os.SOCK_NONBLOCK) catch |err| switch (err) {
+ error.WouldBlock => {
+ self.waitUntilFdReadable(sockfd);
+ continue;
+ },
+ else => return err,
+ };
+ }
+ }
+
+ pub fn connect(self: *Loop, sockfd: os.socket_t, sock_addr: *const os.sockaddr, len: os.socklen_t) os.ConnectError!void {
+ os.connect(sockfd, sock_addr, len) catch |err| switch (err) {
+ error.WouldBlock => {
+ self.waitUntilFdWritable(sockfd);
+ return os.getsockoptError(sockfd);
+ },
+ else => return err,
+ };
+ }
+
/// Performs an async `os.open` using a separate thread.
pub fn openZ(self: *Loop, file_path: [*:0]const u8, flags: u32, mode: os.mode_t) os.OpenError!os.fd_t {
var req_node = Request.Node{
@@ -773,152 +823,309 @@ pub const Loop = struct {
/// Performs an async `os.read` using a separate thread.
/// `fd` must block and not return EAGAIN.
- pub fn read(self: *Loop, fd: os.fd_t, buf: []u8) os.ReadError!usize {
- var req_node = Request.Node{
- .data = .{
- .msg = .{
- .read = .{
- .fd = fd,
- .buf = buf,
- .result = undefined,
+ pub fn read(self: *Loop, fd: os.fd_t, buf: []u8, simulate_evented: bool) os.ReadError!usize {
+ if (simulate_evented) {
+ var req_node = Request.Node{
+ .data = .{
+ .msg = .{
+ .read = .{
+ .fd = fd,
+ .buf = buf,
+ .result = undefined,
+ },
},
+ .finish = .{ .TickNode = .{ .data = @frame() } },
},
- .finish = .{ .TickNode = .{ .data = @frame() } },
- },
- };
- suspend {
- self.posixFsRequest(&req_node);
+ };
+ suspend {
+ self.posixFsRequest(&req_node);
+ }
+ return req_node.data.msg.read.result;
+ } else {
+ while (true) {
+ return os.read(fd, buf) catch |err| switch (err) {
+ error.WouldBlock => {
+ self.waitUntilFdReadable(fd);
+ continue;
+ },
+ else => return err,
+ };
+ }
}
- return req_node.data.msg.read.result;
}
/// Performs an async `os.readv` using a separate thread.
/// `fd` must block and not return EAGAIN.
- pub fn readv(self: *Loop, fd: os.fd_t, iov: []const os.iovec) os.ReadError!usize {
- var req_node = Request.Node{
- .data = .{
- .msg = .{
- .readv = .{
- .fd = fd,
- .iov = iov,
- .result = undefined,
+ pub fn readv(self: *Loop, fd: os.fd_t, iov: []const os.iovec, simulate_evented: bool) os.ReadError!usize {
+ if (simulate_evented) {
+ var req_node = Request.Node{
+ .data = .{
+ .msg = .{
+ .readv = .{
+ .fd = fd,
+ .iov = iov,
+ .result = undefined,
+ },
},
+ .finish = .{ .TickNode = .{ .data = @frame() } },
},
- .finish = .{ .TickNode = .{ .data = @frame() } },
- },
- };
- suspend {
- self.posixFsRequest(&req_node);
+ };
+ suspend {
+ self.posixFsRequest(&req_node);
+ }
+ return req_node.data.msg.readv.result;
+ } else {
+ while (true) {
+ return os.readv(fd, iov) catch |err| switch (err) {
+ error.WouldBlock => {
+ self.waitUntilFdReadable(fd);
+ continue;
+ },
+ else => return err,
+ };
+ }
}
- return req_node.data.msg.readv.result;
}
/// Performs an async `os.pread` using a separate thread.
/// `fd` must block and not return EAGAIN.
- pub fn pread(self: *Loop, fd: os.fd_t, buf: []u8, offset: u64) os.PReadError!usize {
- var req_node = Request.Node{
- .data = .{
- .msg = .{
- .pread = .{
- .fd = fd,
- .buf = buf,
- .offset = offset,
- .result = undefined,
+ pub fn pread(self: *Loop, fd: os.fd_t, buf: []u8, offset: u64, simulate_evented: bool) os.PReadError!usize {
+ if (simulate_evented) {
+ var req_node = Request.Node{
+ .data = .{
+ .msg = .{
+ .pread = .{
+ .fd = fd,
+ .buf = buf,
+ .offset = offset,
+ .result = undefined,
+ },
},
+ .finish = .{ .TickNode = .{ .data = @frame() } },
},
- .finish = .{ .TickNode = .{ .data = @frame() } },
- },
- };
- suspend {
- self.posixFsRequest(&req_node);
+ };
+ suspend {
+ self.posixFsRequest(&req_node);
+ }
+ return req_node.data.msg.pread.result;
+ } else {
+ while (true) {
+ return os.pread(fd, buf, offset) catch |err| switch (err) {
+ error.WouldBlock => {
+ self.waitUntilFdReadable(fd);
+ continue;
+ },
+ else => return err,
+ };
+ }
}
- return req_node.data.msg.pread.result;
}
/// Performs an async `os.preadv` using a separate thread.
/// `fd` must block and not return EAGAIN.
- pub fn preadv(self: *Loop, fd: os.fd_t, iov: []const os.iovec, offset: u64) os.ReadError!usize {
- var req_node = Request.Node{
- .data = .{
- .msg = .{
- .preadv = .{
- .fd = fd,
- .iov = iov,
- .offset = offset,
- .result = undefined,
+ pub fn preadv(self: *Loop, fd: os.fd_t, iov: []const os.iovec, offset: u64, simulate_evented: bool) os.ReadError!usize {
+ if (simulate_evented) {
+ var req_node = Request.Node{
+ .data = .{
+ .msg = .{
+ .preadv = .{
+ .fd = fd,
+ .iov = iov,
+ .offset = offset,
+ .result = undefined,
+ },
},
+ .finish = .{ .TickNode = .{ .data = @frame() } },
},
- .finish = .{ .TickNode = .{ .data = @frame() } },
- },
- };
- suspend {
- self.posixFsRequest(&req_node);
+ };
+ suspend {
+ self.posixFsRequest(&req_node);
+ }
+ return req_node.data.msg.preadv.result;
+ } else {
+ while (true) {
+ return os.preadv(fd, iov, offset) catch |err| switch (err) {
+ error.WouldBlock => {
+ self.waitUntilFdReadable(fd);
+ continue;
+ },
+ else => return err,
+ };
+ }
}
- return req_node.data.msg.preadv.result;
}
/// Performs an async `os.write` using a separate thread.
/// `fd` must block and not return EAGAIN.
- pub fn write(self: *Loop, fd: os.fd_t, bytes: []const u8) os.WriteError!usize {
- var req_node = Request.Node{
- .data = .{
- .msg = .{
- .write = .{
- .fd = fd,
- .bytes = bytes,
- .result = undefined,
+ pub fn write(self: *Loop, fd: os.fd_t, bytes: []const u8, simulate_evented: bool) os.WriteError!usize {
+ if (simulate_evented) {
+ var req_node = Request.Node{
+ .data = .{
+ .msg = .{
+ .write = .{
+ .fd = fd,
+ .bytes = bytes,
+ .result = undefined,
+ },
},
+ .finish = .{ .TickNode = .{ .data = @frame() } },
},
- .finish = .{ .TickNode = .{ .data = @frame() } },
- },
- };
- suspend {
- self.posixFsRequest(&req_node);
+ };
+ suspend {
+ self.posixFsRequest(&req_node);
+ }
+ return req_node.data.msg.write.result;
+ } else {
+ while (true) {
+ return os.write(fd, bytes) catch |err| switch (err) {
+ error.WouldBlock => {
+ self.waitUntilFdWritable(fd);
+ continue;
+ },
+ else => return err,
+ };
+ }
}
- return req_node.data.msg.write.result;
}
/// Performs an async `os.writev` using a separate thread.
/// `fd` must block and not return EAGAIN.
- pub fn writev(self: *Loop, fd: os.fd_t, iov: []const os.iovec_const) os.WriteError!usize {
- var req_node = Request.Node{
- .data = .{
- .msg = .{
- .writev = .{
- .fd = fd,
- .iov = iov,
- .result = undefined,
+ pub fn writev(self: *Loop, fd: os.fd_t, iov: []const os.iovec_const, simulate_evented: bool) os.WriteError!usize {
+ if (simulate_evented) {
+ var req_node = Request.Node{
+ .data = .{
+ .msg = .{
+ .writev = .{
+ .fd = fd,
+ .iov = iov,
+ .result = undefined,
+ },
},
+ .finish = .{ .TickNode = .{ .data = @frame() } },
},
- .finish = .{ .TickNode = .{ .data = @frame() } },
- },
- };
- suspend {
- self.posixFsRequest(&req_node);
+ };
+ suspend {
+ self.posixFsRequest(&req_node);
+ }
+ return req_node.data.msg.writev.result;
+ } else {
+ while (true) {
+ return os.writev(fd, iov) catch |err| switch (err) {
+ error.WouldBlock => {
+ self.waitUntilFdWritable(fd);
+ continue;
+ },
+ else => return err,
+ };
+ }
+ }
+ }
+
+ /// Performs an async `os.pwrite` using a separate thread.
+ /// `fd` must block and not return EAGAIN.
+ pub fn pwrite(self: *Loop, fd: os.fd_t, bytes: []const u8, offset: u64, simulate_evented: bool) os.PerformsWriteError!usize {
+ if (simulate_evented) {
+ var req_node = Request.Node{
+ .data = .{
+ .msg = .{
+ .pwrite = .{
+ .fd = fd,
+ .bytes = bytes,
+ .offset = offset,
+ .result = undefined,
+ },
+ },
+ .finish = .{ .TickNode = .{ .data = @frame() } },
+ },
+ };
+ suspend {
+ self.posixFsRequest(&req_node);
+ }
+ return req_node.data.msg.pwrite.result;
+ } else {
+ while (true) {
+ return os.pwrite(fd, bytes, offset) catch |err| switch (err) {
+ error.WouldBlock => {
+ self.waitUntilFdWritable(fd);
+ continue;
+ },
+ else => return err,
+ };
+ }
}
- return req_node.data.msg.writev.result;
}
/// Performs an async `os.pwritev` using a separate thread.
/// `fd` must block and not return EAGAIN.
- pub fn pwritev(self: *Loop, fd: os.fd_t, iov: []const os.iovec_const, offset: u64) os.WriteError!usize {
- var req_node = Request.Node{
- .data = .{
- .msg = .{
- .pwritev = .{
- .fd = fd,
- .iov = iov,
- .offset = offset,
- .result = undefined,
+ pub fn pwritev(self: *Loop, fd: os.fd_t, iov: []const os.iovec_const, offset: u64, simulate_evented: bool) os.PWriteError!usize {
+ if (simulate_evented) {
+ var req_node = Request.Node{
+ .data = .{
+ .msg = .{
+ .pwritev = .{
+ .fd = fd,
+ .iov = iov,
+ .offset = offset,
+ .result = undefined,
+ },
},
+ .finish = .{ .TickNode = .{ .data = @frame() } },
},
- .finish = .{ .TickNode = .{ .data = @frame() } },
- },
- };
- suspend {
- self.posixFsRequest(&req_node);
+ };
+ suspend {
+ self.posixFsRequest(&req_node);
+ }
+ return req_node.data.msg.pwritev.result;
+ } else {
+ while (true) {
+ return os.pwritev(fd, iov, offset) catch |err| switch (err) {
+ error.WouldBlock => {
+ self.waitUntilFdWritable(fd);
+ continue;
+ },
+ else => return err,
+ };
+ }
+ }
+ }
+
+ pub fn sendto(
+ self: *Loop,
+ /// The file descriptor of the sending socket.
+ sockfd: os.fd_t,
+ /// Message to send.
+ buf: []const u8,
+ flags: u32,
+ dest_addr: ?*const os.sockaddr,
+ addrlen: os.socklen_t,
+ ) os.SendError!usize {
+ while (true) {
+ return os.sendto(sockfd, buf, flags, dest_addr, addrlen) catch |err| switch (err) {
+ error.WouldBlock => {
+ self.waitUntilFdWritable(sockfd);
+ continue;
+ },
+ else => return err,
+ };
+ }
+ }
+
+ pub fn recvfrom(
+ sockfd: os.fd_t,
+ buf: []u8,
+ flags: u32,
+ src_addr: ?*os.sockaddr,
+ addrlen: ?*os.socklen_t,
+ ) os.RecvFromError!usize {
+ while (true) {
+ return os.recvfrom(sockfd, buf, flags, src_addr, addrlen) catch |err| switch (err) {
+ error.WouldBlock => {
+ self.waitUntilFdReadable(sockfd);
+ continue;
+ },
+ else => return err,
+ };
}
- return req_node.data.msg.pwritev.result;
}
/// Performs an async `os.faccessatZ` using a separate thread.
@@ -1073,6 +1280,9 @@ pub const Loop = struct {
.writev => |*msg| {
msg.result = os.writev(msg.fd, msg.iov);
},
+ .pwrite => |*msg| {
+ msg.result = os.pwrite(msg.fd, msg.bytes, msg.offset);
+ },
.pwritev => |*msg| {
msg.result = os.pwritev(msg.fd, msg.iov, msg.offset);
},
@@ -1142,6 +1352,7 @@ pub const Loop = struct {
readv: ReadV,
write: Write,
writev: WriteV,
+ pwrite: PWrite,
pwritev: PWriteV,
pread: PRead,
preadv: PReadV,
@@ -1185,6 +1396,15 @@ pub const Loop = struct {
pub const Error = os.WriteError;
};
+ pub const PWrite = struct {
+ fd: os.fd_t,
+ bytes: []const u8,
+ offset: usize,
+ result: Error!usize,
+
+ pub const Error = os.PWriteError;
+ };
+
pub const PWriteV = struct {
fd: os.fd_t,
iov: []const os.iovec_const,
diff --git a/lib/std/fifo.zig b/lib/std/fifo.zig
index c92da615f9..91d4c0330b 100644
--- a/lib/std/fifo.zig
+++ b/lib/std/fifo.zig
@@ -186,7 +186,9 @@ pub fn LinearFifo(
} else {
var head = self.head + count;
if (powers_of_two) {
- head &= self.buf.len - 1;
+ // Note it is safe to do a wrapping subtract as
+ // bitwise & with all 1s is a noop
+ head &= self.buf.len -% 1;
} else {
head %= self.buf.len;
}
@@ -376,6 +378,14 @@ pub fn LinearFifo(
};
}
+test "LinearFifo(u8, .Dynamic) discard(0) from empty buffer should not error on overflow" {
+ var fifo = LinearFifo(u8, .Dynamic).init(testing.allocator);
+ defer fifo.deinit();
+
+ // If overflow is not explicitly allowed this will crash in debug / safe mode
+ fifo.discard(0);
+}
+
test "LinearFifo(u8, .Dynamic)" {
var fifo = LinearFifo(u8, .Dynamic).init(testing.allocator);
defer fifo.deinit();
diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig
index 73babf5fa2..8d4f5df2e8 100644
--- a/lib/std/fs/file.zig
+++ b/lib/std/fs/file.zig
@@ -414,10 +414,12 @@ pub const File = struct {
pub fn read(self: File, buffer: []u8) ReadError!usize {
if (is_windows) {
return windows.ReadFile(self.handle, buffer, null, self.intended_io_mode);
- } else if (self.capable_io_mode != self.intended_io_mode) {
- return std.event.Loop.instance.?.read(self.handle, buffer);
- } else {
+ }
+
+ if (self.intended_io_mode == .blocking) {
return os.read(self.handle, buffer);
+ } else {
+ return std.event.Loop.instance.?.read(self.handle, buffer, self.capable_io_mode != self.intended_io_mode);
}
}
@@ -436,10 +438,12 @@ pub const File = struct {
pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {
if (is_windows) {
return windows.ReadFile(self.handle, buffer, offset, self.intended_io_mode);
- } else if (self.capable_io_mode != self.intended_io_mode) {
- return std.event.Loop.instance.?.pread(self.handle, buffer, offset);
- } else {
+ }
+
+ if (self.intended_io_mode == .blocking) {
return os.pread(self.handle, buffer, offset);
+ } else {
+ return std.event.Loop.instance.?.pread(self.handle, buffer, offset, self.capable_io_mode != self.intended_io_mode);
}
}
@@ -461,10 +465,12 @@ pub const File = struct {
if (iovecs.len == 0) return @as(usize, 0);
const first = iovecs[0];
return windows.ReadFile(self.handle, first.iov_base[0..first.iov_len], null, self.intended_io_mode);
- } else if (self.capable_io_mode != self.intended_io_mode) {
- return std.event.Loop.instance.?.readv(self.handle, iovecs);
- } else {
+ }
+
+ if (self.intended_io_mode == .blocking) {
return os.readv(self.handle, iovecs);
+ } else {
+ return std.event.Loop.instance.?.readv(self.handle, iovecs, self.capable_io_mode != self.intended_io_mode);
}
}
@@ -500,10 +506,12 @@ pub const File = struct {
if (iovecs.len == 0) return @as(usize, 0);
const first = iovecs[0];
return windows.ReadFile(self.handle, first.iov_base[0..first.iov_len], offset, self.intended_io_mode);
- } else if (self.capable_io_mode != self.intended_io_mode) {
- return std.event.Loop.instance.?.preadv(self.handle, iovecs, offset);
- } else {
+ }
+
+ if (self.intended_io_mode == .blocking) {
return os.preadv(self.handle, iovecs, offset);
+ } else {
+ return std.event.Loop.instance.?.preadv(self.handle, iovecs, offset, self.capable_io_mode != self.intended_io_mode);
}
}
@@ -539,10 +547,12 @@ pub const File = struct {
pub fn write(self: File, bytes: []const u8) WriteError!usize {
if (is_windows) {
return windows.WriteFile(self.handle, bytes, null, self.intended_io_mode);
- } else if (self.capable_io_mode != self.intended_io_mode) {
- return std.event.Loop.instance.?.write(self.handle, bytes);
- } else {
+ }
+
+ if (self.intended_io_mode == .blocking) {
return os.write(self.handle, bytes);
+ } else {
+ return std.event.Loop.instance.?.write(self.handle, bytes, self.capable_io_mode != self.intended_io_mode);
}
}
@@ -556,10 +566,12 @@ pub const File = struct {
pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize {
if (is_windows) {
return windows.WriteFile(self.handle, bytes, offset, self.intended_io_mode);
- } else if (self.capable_io_mode != self.intended_io_mode) {
- return std.event.Loop.instance.?.pwrite(self.handle, bytes, offset);
- } else {
+ }
+
+ if (self.intended_io_mode == .blocking) {
return os.pwrite(self.handle, bytes, offset);
+ } else {
+ return std.event.Loop.instance.?.pwrite(self.handle, bytes, offset, self.capable_io_mode != self.intended_io_mode);
}
}
@@ -576,10 +588,12 @@ pub const File = struct {
if (iovecs.len == 0) return @as(usize, 0);
const first = iovecs[0];
return windows.WriteFile(self.handle, first.iov_base[0..first.iov_len], null, self.intended_io_mode);
- } else if (self.capable_io_mode != self.intended_io_mode) {
- return std.event.Loop.instance.?.writev(self.handle, iovecs);
- } else {
+ }
+
+ if (self.intended_io_mode == .blocking) {
return os.writev(self.handle, iovecs);
+ } else {
+ return std.event.Loop.instance.?.writev(self.handle, iovecs, self.capable_io_mode != self.intended_io_mode);
}
}
@@ -607,10 +621,12 @@ pub const File = struct {
if (iovecs.len == 0) return @as(usize, 0);
const first = iovecs[0];
return windows.WriteFile(self.handle, first.iov_base[0..first.iov_len], offset, self.intended_io_mode);
- } else if (self.capable_io_mode != self.intended_io_mode) {
- return std.event.Loop.instance.?.pwritev(self.handle, iovecs, offset);
- } else {
+ }
+
+ if (self.intended_io_mode == .blocking) {
return os.pwritev(self.handle, iovecs, offset);
+ } else {
+ return std.event.Loop.instance.?.pwritev(self.handle, iovecs, offset, self.capable_io_mode != self.intended_io_mode);
}
}
diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig
index b3cc1fe569..60cd380444 100644
--- a/lib/std/fs/test.zig
+++ b/lib/std/fs/test.zig
@@ -274,6 +274,32 @@ test "file operations on directories" {
dir.close();
}
+test "deleteDir" {
+ var tmp_dir = tmpDir(.{});
+ defer tmp_dir.cleanup();
+
+ // deleting a non-existent directory
+ testing.expectError(error.FileNotFound, tmp_dir.dir.deleteDir("test_dir"));
+
+ var dir = try tmp_dir.dir.makeOpenPath("test_dir", .{});
+ var file = try dir.createFile("test_file", .{});
+ file.close();
+ dir.close();
+
+ // deleting a non-empty directory
+ // TODO: Re-enable this check on Windows, see https://github.com/ziglang/zig/issues/5537
+ if (builtin.os.tag != .windows) {
+ testing.expectError(error.DirNotEmpty, tmp_dir.dir.deleteDir("test_dir"));
+ }
+
+ dir = try tmp_dir.dir.openDir("test_dir", .{});
+ try dir.deleteFile("test_file");
+ dir.close();
+
+ // deleting an empty directory
+ try tmp_dir.dir.deleteDir("test_dir");
+}
+
test "Dir.rename files" {
var tmp_dir = tmpDir(.{});
defer tmp_dir.cleanup();
diff --git a/lib/std/heap.zig b/lib/std/heap.zig
index 16de215cc2..cf32cff645 100644
--- a/lib/std/heap.zig
+++ b/lib/std/heap.zig
@@ -919,6 +919,13 @@ pub fn testAllocator(base_allocator: *mem.Allocator) !void {
const zero_bit_ptr = try allocator.create(u0);
zero_bit_ptr.* = 0;
allocator.destroy(zero_bit_ptr);
+
+ const oversize = try allocator.allocAdvanced(u32, null, 5, .at_least);
+ testing.expect(oversize.len >= 5);
+ for (oversize) |*item| {
+ item.* = 0xDEADBEEF;
+ }
+ allocator.free(oversize);
}
pub fn testAllocatorAligned(base_allocator: *mem.Allocator, comptime alignment: u29) !void {
diff --git a/lib/std/heap/arena_allocator.zig b/lib/std/heap/arena_allocator.zig
index 0737cb2ef8..b7ee1d54c1 100644
--- a/lib/std/heap/arena_allocator.zig
+++ b/lib/std/heap/arena_allocator.zig
@@ -75,13 +75,22 @@ pub const ArenaAllocator = struct {
const adjusted_addr = mem.alignForward(addr, ptr_align);
const adjusted_index = self.state.end_index + (adjusted_addr - addr);
const new_end_index = adjusted_index + n;
- if (new_end_index > cur_buf.len) {
- cur_node = try self.createNode(cur_buf.len, n + ptr_align);
- continue;
+
+ if (new_end_index <= cur_buf.len) {
+ const result = cur_buf[adjusted_index..new_end_index];
+ self.state.end_index = new_end_index;
+ return result;
}
- const result = cur_buf[adjusted_index..new_end_index];
- self.state.end_index = new_end_index;
- return result;
+
+ const bigger_buf_size = @sizeOf(BufNode) + new_end_index;
+ // Try to grow the buffer in-place
+ cur_node.data = self.child_allocator.resize(cur_node.data, bigger_buf_size) catch |err| switch (err) {
+ error.OutOfMemory => {
+ // Allocate a new node if that's not possible
+ cur_node = try self.createNode(cur_buf.len, n + ptr_align);
+ continue;
+ },
+ };
}
}
diff --git a/lib/std/http.zig b/lib/std/http.zig
deleted file mode 100644
index 2b8495db71..0000000000
--- a/lib/std/http.zig
+++ /dev/null
@@ -1,10 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 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.
-test "std.http" {
- _ = @import("http/headers.zig");
-}
-
-pub const Headers = @import("http/headers.zig").Headers;
diff --git a/lib/std/http/headers.zig b/lib/std/http/headers.zig
deleted file mode 100644
index 0ce642865c..0000000000
--- a/lib/std/http/headers.zig
+++ /dev/null
@@ -1,597 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 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.
-// HTTP Header data structure/type
-// Based on lua-http's http.header module
-//
-// Design criteria:
-// - the same header field is allowed more than once
-// - must be able to fetch separate occurrences (important for some headers e.g. Set-Cookie)
-// - optionally available as comma separated list
-// - http2 adds flag to headers that they should never be indexed
-// - header order should be recoverable
-//
-// Headers are implemented as an array of entries.
-// An index of field name => array indices is kept.
-
-const std = @import("../std.zig");
-const debug = std.debug;
-const assert = debug.assert;
-const testing = std.testing;
-const mem = std.mem;
-const Allocator = mem.Allocator;
-
-fn never_index_default(name: []const u8) bool {
- if (mem.eql(u8, "authorization", name)) return true;
- if (mem.eql(u8, "proxy-authorization", name)) return true;
- if (mem.eql(u8, "cookie", name)) return true;
- if (mem.eql(u8, "set-cookie", name)) return true;
- return false;
-}
-
-const HeaderEntry = struct {
- name: []const u8,
- value: []u8,
- never_index: bool,
-
- const Self = @This();
-
- fn init(allocator: *Allocator, name: []const u8, value: []const u8, never_index: ?bool) !Self {
- return Self{
- .name = name, // takes reference
- .value = try allocator.dupe(u8, value),
- .never_index = never_index orelse never_index_default(name),
- };
- }
-
- fn deinit(self: Self, allocator: *Allocator) void {
- allocator.free(self.value);
- }
-
- pub fn modify(self: *Self, allocator: *Allocator, value: []const u8, never_index: ?bool) !void {
- const old_len = self.value.len;
- if (value.len > old_len) {
- self.value = try allocator.realloc(self.value, value.len);
- } else if (value.len < old_len) {
- self.value = allocator.shrink(self.value, value.len);
- }
- mem.copy(u8, self.value, value);
- self.never_index = never_index orelse never_index_default(self.name);
- }
-
- fn compare(context: void, a: HeaderEntry, b: HeaderEntry) bool {
- if (a.name.ptr != b.name.ptr and a.name.len != b.name.len) {
- // Things beginning with a colon *must* be before others
- const a_is_colon = a.name[0] == ':';
- const b_is_colon = b.name[0] == ':';
- if (a_is_colon and !b_is_colon) {
- return true;
- } else if (!a_is_colon and b_is_colon) {
- return false;
- }
-
- // Sort lexicographically on header name
- return mem.order(u8, a.name, b.name) == .lt;
- }
-
- // Sort lexicographically on header value
- if (!mem.eql(u8, a.value, b.value)) {
- return mem.order(u8, a.value, b.value) == .lt;
- }
-
- // Doesn't matter here; need to pick something for sort consistency
- return a.never_index;
- }
-};
-
-test "HeaderEntry" {
- var e = try HeaderEntry.init(testing.allocator, "foo", "bar", null);
- defer e.deinit(testing.allocator);
- testing.expectEqualSlices(u8, "foo", e.name);
- testing.expectEqualSlices(u8, "bar", e.value);
- testing.expectEqual(false, e.never_index);
-
- try e.modify(testing.allocator, "longer value", null);
- testing.expectEqualSlices(u8, "longer value", e.value);
-
- // shorter value
- try e.modify(testing.allocator, "x", null);
- testing.expectEqualSlices(u8, "x", e.value);
-}
-
-const HeaderList = std.ArrayListUnmanaged(HeaderEntry);
-const HeaderIndexList = std.ArrayListUnmanaged(usize);
-const HeaderIndex = std.StringHashMapUnmanaged(HeaderIndexList);
-
-pub const Headers = struct {
- // the owned header field name is stored in the index as part of the key
- allocator: *Allocator,
- data: HeaderList,
- index: HeaderIndex,
-
- const Self = @This();
-
- pub fn init(allocator: *Allocator) Self {
- return Self{
- .allocator = allocator,
- .data = HeaderList{},
- .index = HeaderIndex{},
- };
- }
-
- pub fn deinit(self: *Self) void {
- {
- var it = self.index.iterator();
- while (it.next()) |entry| {
- entry.value.deinit(self.allocator);
- self.allocator.free(entry.key);
- }
- self.index.deinit(self.allocator);
- }
- {
- for (self.data.items) |entry| {
- entry.deinit(self.allocator);
- }
- self.data.deinit(self.allocator);
- }
- self.* = undefined;
- }
-
- pub fn clone(self: Self, allocator: *Allocator) !Self {
- var other = Headers.init(allocator);
- errdefer other.deinit();
- try other.data.ensureCapacity(allocator, self.data.items.len);
- try other.index.initCapacity(allocator, self.index.entries.len);
- for (self.data.items) |entry| {
- try other.append(entry.name, entry.value, entry.never_index);
- }
- return other;
- }
-
- pub fn toSlice(self: Self) []const HeaderEntry {
- return self.data.items;
- }
-
- pub fn append(self: *Self, name: []const u8, value: []const u8, never_index: ?bool) !void {
- const n = self.data.items.len + 1;
- try self.data.ensureCapacity(self.allocator, n);
- var entry: HeaderEntry = undefined;
- if (self.index.getEntry(name)) |kv| {
- entry = try HeaderEntry.init(self.allocator, kv.key, value, never_index);
- errdefer entry.deinit(self.allocator);
- const dex = &kv.value;
- try dex.append(self.allocator, n - 1);
- } else {
- const name_dup = try self.allocator.dupe(u8, name);
- errdefer self.allocator.free(name_dup);
- entry = try HeaderEntry.init(self.allocator, name_dup, value, never_index);
- errdefer entry.deinit(self.allocator);
- var dex = HeaderIndexList{};
- try dex.append(self.allocator, n - 1);
- errdefer dex.deinit(self.allocator);
- _ = try self.index.put(self.allocator, name_dup, dex);
- }
- self.data.appendAssumeCapacity(entry);
- }
-
- /// If the header already exists, replace the current value, otherwise append it to the list of headers.
- /// If the header has multiple entries then returns an error.
- pub fn upsert(self: *Self, name: []const u8, value: []const u8, never_index: ?bool) !void {
- if (self.index.get(name)) |kv| {
- const dex = kv.value;
- if (dex.len != 1)
- return error.CannotUpsertMultiValuedField;
- var e = &self.data.at(dex.at(0));
- try e.modify(value, never_index);
- } else {
- try self.append(name, value, never_index);
- }
- }
-
- /// Returns boolean indicating if the field is present.
- pub fn contains(self: Self, name: []const u8) bool {
- return self.index.contains(name);
- }
-
- /// Returns boolean indicating if something was deleted.
- pub fn delete(self: *Self, name: []const u8) bool {
- if (self.index.remove(name)) |*kv| {
- const dex = &kv.value;
- // iterate backwards
- var i = dex.items.len;
- while (i > 0) {
- i -= 1;
- const data_index = dex.items[i];
- const removed = self.data.orderedRemove(data_index);
- assert(mem.eql(u8, removed.name, name));
- removed.deinit(self.allocator);
- }
- dex.deinit(self.allocator);
- self.allocator.free(kv.key);
- self.rebuildIndex();
- return true;
- } else {
- return false;
- }
- }
-
- /// Removes the element at the specified index.
- /// Moves items down to fill the empty space.
- /// TODO this implementation can be replaced by adding
- /// orderedRemove to the new hash table implementation as an
- /// alternative to swapRemove.
- pub fn orderedRemove(self: *Self, i: usize) void {
- const removed = self.data.orderedRemove(i);
- const kv = self.index.getEntry(removed.name).?;
- const dex = &kv.value;
- if (dex.items.len == 1) {
- // was last item; delete the index
- dex.deinit(self.allocator);
- removed.deinit(self.allocator);
- const key = kv.key;
- _ = self.index.remove(key); // invalidates `kv` and `dex`
- self.allocator.free(key);
- } else {
- dex.shrink(self.allocator, dex.items.len - 1);
- removed.deinit(self.allocator);
- }
- // if it was the last item; no need to rebuild index
- if (i != self.data.items.len) {
- self.rebuildIndex();
- }
- }
-
- /// Removes the element at the specified index.
- /// The empty slot is filled from the end of the list.
- /// TODO this implementation can be replaced by simply using the
- /// new hash table which does swap removal.
- pub fn swapRemove(self: *Self, i: usize) void {
- const removed = self.data.swapRemove(i);
- const kv = self.index.getEntry(removed.name).?;
- const dex = &kv.value;
- if (dex.items.len == 1) {
- // was last item; delete the index
- dex.deinit(self.allocator);
- removed.deinit(self.allocator);
- const key = kv.key;
- _ = self.index.remove(key); // invalidates `kv` and `dex`
- self.allocator.free(key);
- } else {
- dex.shrink(self.allocator, dex.items.len - 1);
- removed.deinit(self.allocator);
- }
- // if it was the last item; no need to rebuild index
- if (i != self.data.items.len) {
- self.rebuildIndex();
- }
- }
-
- /// Access the header at the specified index.
- pub fn at(self: Self, i: usize) HeaderEntry {
- return self.data.items[i];
- }
-
- /// Returns a list of indices containing headers with the given name.
- /// The returned list should not be modified by the caller.
- pub fn getIndices(self: Self, name: []const u8) ?HeaderIndexList {
- return self.index.get(name);
- }
-
- /// Returns a slice containing each header with the given name.
- pub fn get(self: Self, allocator: *Allocator, name: []const u8) !?[]const HeaderEntry {
- const dex = self.getIndices(name) orelse return null;
-
- const buf = try allocator.alloc(HeaderEntry, dex.items.len);
- var n: usize = 0;
- for (dex.items) |idx| {
- buf[n] = self.data.items[idx];
- n += 1;
- }
- return buf;
- }
-
- /// Returns all headers with the given name as a comma separated string.
- ///
- /// Useful for HTTP headers that follow RFC-7230 section 3.2.2:
- /// A recipient MAY combine multiple header fields with the same field
- /// name into one "field-name: field-value" pair, without changing the
- /// semantics of the message, by appending each subsequent field value to
- /// the combined field value in order, separated by a comma. The order
- /// in which header fields with the same field name are received is
- /// therefore significant to the interpretation of the combined field
- /// value
- pub fn getCommaSeparated(self: Self, allocator: *Allocator, name: []const u8) !?[]u8 {
- const dex = self.getIndices(name) orelse return null;
-
- // adapted from mem.join
- const total_len = blk: {
- var sum: usize = dex.items.len - 1; // space for separator(s)
- for (dex.items) |idx|
- sum += self.data.items[idx].value.len;
- break :blk sum;
- };
-
- const buf = try allocator.alloc(u8, total_len);
- errdefer allocator.free(buf);
-
- const first_value = self.data.items[dex.items[0]].value;
- mem.copy(u8, buf, first_value);
- var buf_index: usize = first_value.len;
- for (dex.items[1..]) |idx| {
- const value = self.data.items[idx].value;
- buf[buf_index] = ',';
- buf_index += 1;
- mem.copy(u8, buf[buf_index..], value);
- buf_index += value.len;
- }
-
- // No need for shrink since buf is exactly the correct size.
- return buf;
- }
-
- fn rebuildIndex(self: *Self) void {
- // clear out the indexes
- var it = self.index.iterator();
- while (it.next()) |entry| {
- entry.value.shrinkRetainingCapacity(0);
- }
- // fill up indexes again; we know capacity is fine from before
- for (self.data.items) |entry, i| {
- self.index.getEntry(entry.name).?.value.appendAssumeCapacity(i);
- }
- }
-
- pub fn sort(self: *Self) void {
- std.sort.sort(HeaderEntry, self.data.items, {}, HeaderEntry.compare);
- self.rebuildIndex();
- }
-
- pub fn format(
- self: Self,
- comptime fmt: []const u8,
- options: std.fmt.FormatOptions,
- out_stream: anytype,
- ) !void {
- for (self.toSlice()) |entry| {
- try out_stream.writeAll(entry.name);
- try out_stream.writeAll(": ");
- try out_stream.writeAll(entry.value);
- try out_stream.writeAll("\n");
- }
- }
-};
-
-test "Headers.iterator" {
- var h = Headers.init(testing.allocator);
- defer h.deinit();
- try h.append("foo", "bar", null);
- try h.append("cookie", "somevalue", null);
-
- var count: i32 = 0;
- for (h.toSlice()) |e| {
- if (count == 0) {
- testing.expectEqualSlices(u8, "foo", e.name);
- testing.expectEqualSlices(u8, "bar", e.value);
- testing.expectEqual(false, e.never_index);
- } else if (count == 1) {
- testing.expectEqualSlices(u8, "cookie", e.name);
- testing.expectEqualSlices(u8, "somevalue", e.value);
- testing.expectEqual(true, e.never_index);
- }
- count += 1;
- }
- testing.expectEqual(@as(i32, 2), count);
-}
-
-test "Headers.contains" {
- var h = Headers.init(testing.allocator);
- defer h.deinit();
- try h.append("foo", "bar", null);
- try h.append("cookie", "somevalue", null);
-
- testing.expectEqual(true, h.contains("foo"));
- testing.expectEqual(false, h.contains("flooble"));
-}
-
-test "Headers.delete" {
- var h = Headers.init(testing.allocator);
- defer h.deinit();
- try h.append("foo", "bar", null);
- try h.append("baz", "qux", null);
- try h.append("cookie", "somevalue", null);
-
- testing.expectEqual(false, h.delete("not-present"));
- testing.expectEqual(@as(usize, 3), h.toSlice().len);
-
- testing.expectEqual(true, h.delete("foo"));
- testing.expectEqual(@as(usize, 2), h.toSlice().len);
- {
- const e = h.at(0);
- testing.expectEqualSlices(u8, "baz", e.name);
- testing.expectEqualSlices(u8, "qux", e.value);
- testing.expectEqual(false, e.never_index);
- }
- {
- const e = h.at(1);
- testing.expectEqualSlices(u8, "cookie", e.name);
- testing.expectEqualSlices(u8, "somevalue", e.value);
- testing.expectEqual(true, e.never_index);
- }
-
- testing.expectEqual(false, h.delete("foo"));
-}
-
-test "Headers.orderedRemove" {
- var h = Headers.init(testing.allocator);
- defer h.deinit();
- try h.append("foo", "bar", null);
- try h.append("baz", "qux", null);
- try h.append("cookie", "somevalue", null);
-
- h.orderedRemove(0);
- testing.expectEqual(@as(usize, 2), h.toSlice().len);
- {
- const e = h.at(0);
- testing.expectEqualSlices(u8, "baz", e.name);
- testing.expectEqualSlices(u8, "qux", e.value);
- testing.expectEqual(false, e.never_index);
- }
- {
- const e = h.at(1);
- testing.expectEqualSlices(u8, "cookie", e.name);
- testing.expectEqualSlices(u8, "somevalue", e.value);
- testing.expectEqual(true, e.never_index);
- }
-}
-
-test "Headers.swapRemove" {
- var h = Headers.init(testing.allocator);
- defer h.deinit();
- try h.append("foo", "bar", null);
- try h.append("baz", "qux", null);
- try h.append("cookie", "somevalue", null);
-
- h.swapRemove(0);
- testing.expectEqual(@as(usize, 2), h.toSlice().len);
- {
- const e = h.at(0);
- testing.expectEqualSlices(u8, "cookie", e.name);
- testing.expectEqualSlices(u8, "somevalue", e.value);
- testing.expectEqual(true, e.never_index);
- }
- {
- const e = h.at(1);
- testing.expectEqualSlices(u8, "baz", e.name);
- testing.expectEqualSlices(u8, "qux", e.value);
- testing.expectEqual(false, e.never_index);
- }
-}
-
-test "Headers.at" {
- var h = Headers.init(testing.allocator);
- defer h.deinit();
- try h.append("foo", "bar", null);
- try h.append("cookie", "somevalue", null);
-
- {
- const e = h.at(0);
- testing.expectEqualSlices(u8, "foo", e.name);
- testing.expectEqualSlices(u8, "bar", e.value);
- testing.expectEqual(false, e.never_index);
- }
- {
- const e = h.at(1);
- testing.expectEqualSlices(u8, "cookie", e.name);
- testing.expectEqualSlices(u8, "somevalue", e.value);
- testing.expectEqual(true, e.never_index);
- }
-}
-
-test "Headers.getIndices" {
- var h = Headers.init(testing.allocator);
- defer h.deinit();
- try h.append("foo", "bar", null);
- try h.append("set-cookie", "x=1", null);
- try h.append("set-cookie", "y=2", null);
-
- testing.expect(null == h.getIndices("not-present"));
- testing.expectEqualSlices(usize, &[_]usize{0}, h.getIndices("foo").?.items);
- testing.expectEqualSlices(usize, &[_]usize{ 1, 2 }, h.getIndices("set-cookie").?.items);
-}
-
-test "Headers.get" {
- var h = Headers.init(testing.allocator);
- defer h.deinit();
- try h.append("foo", "bar", null);
- try h.append("set-cookie", "x=1", null);
- try h.append("set-cookie", "y=2", null);
-
- {
- const v = try h.get(testing.allocator, "not-present");
- testing.expect(null == v);
- }
- {
- const v = (try h.get(testing.allocator, "foo")).?;
- defer testing.allocator.free(v);
- const e = v[0];
- testing.expectEqualSlices(u8, "foo", e.name);
- testing.expectEqualSlices(u8, "bar", e.value);
- testing.expectEqual(false, e.never_index);
- }
- {
- const v = (try h.get(testing.allocator, "set-cookie")).?;
- defer testing.allocator.free(v);
- {
- const e = v[0];
- testing.expectEqualSlices(u8, "set-cookie", e.name);
- testing.expectEqualSlices(u8, "x=1", e.value);
- testing.expectEqual(true, e.never_index);
- }
- {
- const e = v[1];
- testing.expectEqualSlices(u8, "set-cookie", e.name);
- testing.expectEqualSlices(u8, "y=2", e.value);
- testing.expectEqual(true, e.never_index);
- }
- }
-}
-
-test "Headers.getCommaSeparated" {
- var h = Headers.init(testing.allocator);
- defer h.deinit();
- try h.append("foo", "bar", null);
- try h.append("set-cookie", "x=1", null);
- try h.append("set-cookie", "y=2", null);
-
- {
- const v = try h.getCommaSeparated(testing.allocator, "not-present");
- testing.expect(null == v);
- }
- {
- const v = (try h.getCommaSeparated(testing.allocator, "foo")).?;
- defer testing.allocator.free(v);
- testing.expectEqualSlices(u8, "bar", v);
- }
- {
- const v = (try h.getCommaSeparated(testing.allocator, "set-cookie")).?;
- defer testing.allocator.free(v);
- testing.expectEqualSlices(u8, "x=1,y=2", v);
- }
-}
-
-test "Headers.sort" {
- var h = Headers.init(testing.allocator);
- defer h.deinit();
- try h.append("foo", "bar", null);
- try h.append("cookie", "somevalue", null);
-
- h.sort();
- {
- const e = h.at(0);
- testing.expectEqualSlices(u8, "cookie", e.name);
- testing.expectEqualSlices(u8, "somevalue", e.value);
- testing.expectEqual(true, e.never_index);
- }
- {
- const e = h.at(1);
- testing.expectEqualSlices(u8, "foo", e.name);
- testing.expectEqualSlices(u8, "bar", e.value);
- testing.expectEqual(false, e.never_index);
- }
-}
-
-test "Headers.format" {
- var h = Headers.init(testing.allocator);
- defer h.deinit();
- try h.append("foo", "bar", null);
- try h.append("cookie", "somevalue", null);
-
- var buf: [100]u8 = undefined;
- testing.expectEqualSlices(u8,
- \\foo: bar
- \\cookie: somevalue
- \\
- , try std.fmt.bufPrint(buf[0..], "{}", .{h}));
-}
diff --git a/lib/std/log.zig b/lib/std/log.zig
index 7b677f698a..0cc2b54452 100644
--- a/lib/std/log.zig
+++ b/lib/std/log.zig
@@ -101,14 +101,12 @@ pub const Level = enum {
debug,
};
-/// The default log level is based on build mode. Note that in ReleaseSmall
-/// builds the default level is emerg but no messages will be stored/logged
-/// by the default logger to save space.
+/// The default log level is based on build mode.
pub const default_level: Level = switch (builtin.mode) {
.Debug => .debug,
.ReleaseSafe => .notice,
.ReleaseFast => .err,
- .ReleaseSmall => .emerg,
+ .ReleaseSmall => .err,
};
/// The current log level. This is set to root.log_level if present, otherwise
@@ -131,11 +129,22 @@ fn log(
// On freestanding one must provide a log function; we do not have
// any I/O configured.
return;
- } else if (builtin.mode != .ReleaseSmall) {
+ } else {
+ const level_txt = switch (message_level) {
+ .emerg => "emergency",
+ .alert => "alert",
+ .crit => "critical",
+ .err => "error",
+ .warn => "warning",
+ .notice => "notice",
+ .info => "info",
+ .debug => "debug",
+ };
+ const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
+ const stderr = std.io.getStdErr().writer();
const held = std.debug.getStderrMutex().acquire();
defer held.release();
- const stderr = std.io.getStdErr().writer();
- nosuspend stderr.print(format ++ "\n", args) catch return;
+ nosuspend stderr.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return;
}
}
}
diff --git a/lib/std/mem/Allocator.zig b/lib/std/mem/Allocator.zig
index 326a73b915..4511acb275 100644
--- a/lib/std/mem/Allocator.zig
+++ b/lib/std/mem/Allocator.zig
@@ -231,8 +231,6 @@ fn AllocWithOptionsPayload(comptime Elem: type, comptime alignment: ?u29, compti
/// call `free` when done.
///
/// For allocating a single item, see `create`.
-///
-/// Deprecated; use `allocWithOptions`.
pub fn allocSentinel(
self: *Allocator,
comptime Elem: type,
diff --git a/lib/std/meta.zig b/lib/std/meta.zig
index 1507aa9de8..492e497ff4 100644
--- a/lib/std/meta.zig
+++ b/lib/std/meta.zig
@@ -807,7 +807,7 @@ pub fn sizeof(target: anytype) usize {
// TODO to get the correct result we have to translate
// `1073741824 * 4` as `int(1073741824) *% int(4)` since
// sizeof(1073741824 * 4) != sizeof(4294967296).
-
+
// TODO test if target fits in int, long or long long
return @sizeOf(c_int);
},
@@ -826,3 +826,112 @@ test "sizeof" {
testing.expect(sizeof(E.One) == @sizeOf(c_int));
testing.expect(sizeof(S) == 4);
}
+
+/// For a given function type, returns a tuple type which fields will
+/// correspond to the argument types.
+///
+/// Examples:
+/// - `ArgsTuple(fn() void)` ⇒ `tuple { }`
+/// - `ArgsTuple(fn(a: u32) u32)` ⇒ `tuple { u32 }`
+/// - `ArgsTuple(fn(a: u32, b: f16) noreturn)` ⇒ `tuple { u32, f16 }`
+pub fn ArgsTuple(comptime Function: type) type {
+ const info = @typeInfo(Function);
+ if (info != .Fn)
+ @compileError("ArgsTuple expects a function type");
+
+ const function_info = info.Fn;
+ if (function_info.is_generic)
+ @compileError("Cannot create ArgsTuple for generic function");
+ if (function_info.is_var_args)
+ @compileError("Cannot create ArgsTuple for variadic function");
+
+ var argument_field_list: [function_info.args.len]std.builtin.TypeInfo.StructField = undefined;
+ inline for (function_info.args) |arg, i| {
+ @setEvalBranchQuota(10_000);
+ var num_buf: [128]u8 = undefined;
+ argument_field_list[i] = std.builtin.TypeInfo.StructField{
+ .name = std.fmt.bufPrint(&num_buf, "{d}", .{i}) catch unreachable,
+ .field_type = arg.arg_type.?,
+ .default_value = @as(?(arg.arg_type.?), null),
+ .is_comptime = false,
+ };
+ }
+
+ return @Type(std.builtin.TypeInfo{
+ .Struct = std.builtin.TypeInfo.Struct{
+ .is_tuple = true,
+ .layout = .Auto,
+ .decls = &[_]std.builtin.TypeInfo.Declaration{},
+ .fields = &argument_field_list,
+ },
+ });
+}
+
+/// For a given anonymous list of types, returns a new tuple type
+/// with those types as fields.
+///
+/// Examples:
+/// - `Tuple(&[_]type {})` ⇒ `tuple { }`
+/// - `Tuple(&[_]type {f32})` ⇒ `tuple { f32 }`
+/// - `Tuple(&[_]type {f32,u32})` ⇒ `tuple { f32, u32 }`
+pub fn Tuple(comptime types: []const type) type {
+ var tuple_fields: [types.len]std.builtin.TypeInfo.StructField = undefined;
+ inline for (types) |T, i| {
+ @setEvalBranchQuota(10_000);
+ var num_buf: [128]u8 = undefined;
+ tuple_fields[i] = std.builtin.TypeInfo.StructField{
+ .name = std.fmt.bufPrint(&num_buf, "{d}", .{i}) catch unreachable,
+ .field_type = T,
+ .default_value = @as(?T, null),
+ .is_comptime = false,
+ };
+ }
+
+ return @Type(std.builtin.TypeInfo{
+ .Struct = std.builtin.TypeInfo.Struct{
+ .is_tuple = true,
+ .layout = .Auto,
+ .decls = &[_]std.builtin.TypeInfo.Declaration{},
+ .fields = &tuple_fields,
+ },
+ });
+}
+
+const TupleTester = struct {
+ fn assertTypeEqual(comptime Expected: type, comptime Actual: type) void {
+ if (Expected != Actual)
+ @compileError("Expected type " ++ @typeName(Expected) ++ ", but got type " ++ @typeName(Actual));
+ }
+
+ fn assertTuple(comptime expected: anytype, comptime Actual: type) void {
+ const info = @typeInfo(Actual);
+ if (info != .Struct)
+ @compileError("Expected struct type");
+ if (!info.Struct.is_tuple)
+ @compileError("Struct type must be a tuple type");
+
+ const fields_list = std.meta.fields(Actual);
+ if (expected.len != fields_list.len)
+ @compileError("Argument count mismatch");
+
+ inline for (fields_list) |fld, i| {
+ if (expected[i] != fld.field_type) {
+ @compileError("Field " ++ fld.name ++ " expected to be type " ++ @typeName(expected[i]) ++ ", but was type " ++ @typeName(fld.field_type));
+ }
+ }
+ }
+};
+
+test "ArgsTuple" {
+ TupleTester.assertTuple(.{}, ArgsTuple(fn () void));
+ TupleTester.assertTuple(.{u32}, ArgsTuple(fn (a: u32) []const u8));
+ TupleTester.assertTuple(.{ u32, f16 }, ArgsTuple(fn (a: u32, b: f16) noreturn));
+ TupleTester.assertTuple(.{ u32, f16, []const u8 }, ArgsTuple(fn (a: u32, b: f16, c: []const u8) noreturn));
+}
+
+test "Tuple" {
+ TupleTester.assertTuple(.{}, Tuple(&[_]type{}));
+ TupleTester.assertTuple(.{u32}, Tuple(&[_]type{u32}));
+ TupleTester.assertTuple(.{ u32, f16 }, Tuple(&[_]type{ u32, f16 }));
+ TupleTester.assertTuple(.{ u32, f16, []const u8 }, Tuple(&[_]type{ u32, f16, []const u8 }));
+}
diff --git a/lib/std/net.zig b/lib/std/net.zig
index 45d8f07f04..fe7d0fafe6 100644
--- a/lib/std/net.zig
+++ b/lib/std/net.zig
@@ -614,11 +614,11 @@ pub fn connectUnixSocket(path: []const u8) !fs.File {
var addr = try std.net.Address.initUnix(path);
- try os.connect(
- sockfd,
- &addr.any,
- addr.getOsSockLen(),
- );
+ if (std.io.is_async) {
+ try loop.connect(sockfd, &addr.any, addr.getOsSockLen());
+ } else {
+ try os.connect(sockfd, &addr.any, addr.getOsSockLen());
+ }
return fs.File{
.handle = sockfd,
@@ -677,7 +677,13 @@ pub fn tcpConnectToAddress(address: Address) !fs.File {
(if (builtin.os.tag == .windows) 0 else os.SOCK_CLOEXEC);
const sockfd = try os.socket(address.any.family, sock_flags, os.IPPROTO_TCP);
errdefer os.close(sockfd);
- try os.connect(sockfd, &address.any, address.getOsSockLen());
+
+ if (std.io.is_async) {
+ const loop = std.event.Loop.instance orelse return error.WouldBlock;
+ try loop.connect(sockfd, &address.any, address.getOsSockLen());
+ } else {
+ try os.connect(sockfd, &address.any, address.getOsSockLen());
+ }
return fs.File{ .handle = sockfd };
}
@@ -1429,7 +1435,11 @@ fn resMSendRc(
if (answers[i].len == 0) {
var j: usize = 0;
while (j < ns.len) : (j += 1) {
- _ = os.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined;
+ if (std.io.is_async) {
+ _ = std.event.Loop.instance.?.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined;
+ } else {
+ _ = os.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined;
+ }
}
}
}
@@ -1444,7 +1454,10 @@ fn resMSendRc(
while (true) {
var sl_copy = sl;
- const rlen = os.recvfrom(fd, answer_bufs[next], 0, &sa.any, &sl_copy) catch break;
+ const rlen = if (std.io.is_async)
+ std.event.Loop.instance.?.recvfrom(fd, answer_bufs[next], 0, &sa.any, &sl_copy) catch break
+ else
+ os.recvfrom(fd, answer_bufs[next], 0, &sa.any, &sl_copy) catch break;
// Ignore non-identifiable packets
if (rlen < 4) continue;
@@ -1470,7 +1483,11 @@ fn resMSendRc(
0, 3 => {},
2 => if (servfail_retry != 0) {
servfail_retry -= 1;
- _ = os.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined;
+ if (std.io.is_async) {
+ _ = std.event.Loop.instance.?.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined;
+ } else {
+ _ = os.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined;
+ }
},
else => continue,
}
@@ -1661,18 +1678,23 @@ pub const StreamServer = struct {
/// If this function succeeds, the returned `Connection` is a caller-managed resource.
pub fn accept(self: *StreamServer) AcceptError!Connection {
- const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
- const accept_flags = nonblock | os.SOCK_CLOEXEC;
var accepted_addr: Address = undefined;
var adr_len: os.socklen_t = @sizeOf(Address);
- if (os.accept(self.sockfd.?, &accepted_addr.any, &adr_len, accept_flags)) |fd| {
+ const accept_result = blk: {
+ if (std.io.is_async) {
+ const loop = std.event.Loop.instance orelse return error.UnexpectedError;
+ break :blk loop.accept(self.sockfd.?, &accepted_addr.any, &adr_len, os.SOCK_CLOEXEC);
+ } else {
+ break :blk os.accept(self.sockfd.?, &accepted_addr.any, &adr_len, os.SOCK_CLOEXEC);
+ }
+ };
+
+ if (accept_result) |fd| {
return Connection{
.file = fs.File{ .handle = fd },
.address = accepted_addr,
};
} else |err| switch (err) {
- // We only give SOCK_NONBLOCK when I/O mode is async, in which case this error
- // is handled by os.accept4.
error.WouldBlock => unreachable,
else => |e| return e,
}
diff --git a/lib/std/os.zig b/lib/std/os.zig
index 0b09b1f82a..c06ce4ed00 100644
--- a/lib/std/os.zig
+++ b/lib/std/os.zig
@@ -314,8 +314,8 @@ pub const ReadError = error{
/// Returns the number of bytes that were read, which can be less than
/// buf.len. If 0 bytes were read, that means EOF.
-/// If the application has a global event loop enabled, EAGAIN is handled
-/// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
+/// If `fd` is opened in non blocking mode, the function will return error.WouldBlock
+/// when EAGAIN is received.
///
/// Linux has a limit on how many bytes may be transferred in one `read` call, which is `0x7ffff000`
/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
@@ -366,12 +366,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
EINTR => continue,
EINVAL => unreachable,
EFAULT => unreachable,
- EAGAIN => if (std.event.Loop.instance) |loop| {
- loop.waitUntilFdReadable(fd);
- continue;
- } else {
- return error.WouldBlock;
- },
+ EAGAIN => return error.WouldBlock,
EBADF => return error.NotOpenForReading, // Can be a race condition.
EIO => return error.InputOutput,
EISDIR => return error.IsDir,
@@ -387,8 +382,8 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
/// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
///
-/// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled
-/// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`.
+/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
+/// return error.WouldBlock when EAGAIN is received.
/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
///
@@ -428,12 +423,7 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {
EINTR => continue,
EINVAL => unreachable,
EFAULT => unreachable,
- EAGAIN => if (std.event.Loop.instance) |loop| {
- loop.waitUntilFdReadable(fd);
- continue;
- } else {
- return error.WouldBlock;
- },
+ EAGAIN => return error.WouldBlock,
EBADF => return error.NotOpenForReading, // can be a race condition
EIO => return error.InputOutput,
EISDIR => return error.IsDir,
@@ -450,8 +440,8 @@ pub const PReadError = ReadError || error{Unseekable};
///
/// Retries when interrupted by a signal.
///
-/// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled
-/// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`.
+/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
+/// return error.WouldBlock when EAGAIN is received.
/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
@@ -492,12 +482,7 @@ pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
EINTR => continue,
EINVAL => unreachable,
EFAULT => unreachable,
- EAGAIN => if (std.event.Loop.instance) |loop| {
- loop.waitUntilFdReadable(fd);
- continue;
- } else {
- return error.WouldBlock;
- },
+ EAGAIN => return error.WouldBlock,
EBADF => return error.NotOpenForReading, // Can be a race condition.
EIO => return error.InputOutput,
EISDIR => return error.IsDir,
@@ -586,8 +571,8 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
///
/// Retries when interrupted by a signal.
///
-/// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled
-/// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`.
+/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
+/// return error.WouldBlock when EAGAIN is received.
/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
///
@@ -637,12 +622,7 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize {
EINTR => continue,
EINVAL => unreachable,
EFAULT => unreachable,
- EAGAIN => if (std.event.Loop.instance) |loop| {
- loop.waitUntilFdReadable(fd);
- continue;
- } else {
- return error.WouldBlock;
- },
+ EAGAIN => return error.WouldBlock,
EBADF => return error.NotOpenForReading, // can be a race condition
EIO => return error.InputOutput,
EISDIR => return error.IsDir,
@@ -687,8 +667,8 @@ pub const WriteError = error{
/// another write() call to transfer the remaining bytes. The subsequent call will either
/// transfer further bytes or may result in an error (e.g., if the disk is now full).
///
-/// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled
-/// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`.
+/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
+/// return error.WouldBlock when EAGAIN is received.
/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
///
@@ -741,12 +721,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
EINTR => continue,
EINVAL => unreachable,
EFAULT => unreachable,
- EAGAIN => if (std.event.Loop.instance) |loop| {
- loop.waitUntilFdWritable(fd);
- continue;
- } else {
- return error.WouldBlock;
- },
+ EAGAIN => return error.WouldBlock,
EBADF => return error.NotOpenForWriting, // can be a race condition.
EDESTADDRREQ => unreachable, // `connect` was never called.
EDQUOT => return error.DiskQuota,
@@ -772,8 +747,8 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
/// another write() call to transfer the remaining bytes. The subsequent call will either
/// transfer further bytes or may result in an error (e.g., if the disk is now full).
///
-/// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled
-/// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`.
+/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
+/// return error.WouldBlock when EAGAIN is received.k`.
/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
///
@@ -814,12 +789,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
EINTR => continue,
EINVAL => unreachable,
EFAULT => unreachable,
- EAGAIN => if (std.event.Loop.instance) |loop| {
- loop.waitUntilFdWritable(fd);
- continue;
- } else {
- return error.WouldBlock;
- },
+ EAGAIN => return error.WouldBlock,
EBADF => return error.NotOpenForWriting, // Can be a race condition.
EDESTADDRREQ => unreachable, // `connect` was never called.
EDQUOT => return error.DiskQuota,
@@ -847,8 +817,8 @@ pub const PWriteError = WriteError || error{Unseekable};
/// another write() call to transfer the remaining bytes. The subsequent call will either
/// transfer further bytes or may result in an error (e.g., if the disk is now full).
///
-/// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled
-/// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`.
+/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
+/// return error.WouldBlock when EAGAIN is received.
/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
///
@@ -905,12 +875,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
EINTR => continue,
EINVAL => unreachable,
EFAULT => unreachable,
- EAGAIN => if (std.event.Loop.instance) |loop| {
- loop.waitUntilFdWritable(fd);
- continue;
- } else {
- return error.WouldBlock;
- },
+ EAGAIN => return error.WouldBlock,
EBADF => return error.NotOpenForWriting, // Can be a race condition.
EDESTADDRREQ => unreachable, // `connect` was never called.
EDQUOT => return error.DiskQuota,
@@ -939,8 +904,8 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
/// another write() call to transfer the remaining bytes. The subsequent call will either
/// transfer further bytes or may result in an error (e.g., if the disk is now full).
///
-/// If the application has a global event loop enabled, EAGAIN is handled
-/// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`.
+/// If `fd` is opened in non blocking mode, the function will
+/// return error.WouldBlock when EAGAIN is received.
///
/// The following systems do not have this syscall, and will return partial writes if more than one
/// vector is provided:
@@ -993,12 +958,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz
EINTR => continue,
EINVAL => unreachable,
EFAULT => unreachable,
- EAGAIN => if (std.event.Loop.instance) |loop| {
- loop.waitUntilFdWritable(fd);
- continue;
- } else {
- return error.WouldBlock;
- },
+ EAGAIN => return error.WouldBlock,
EBADF => return error.NotOpenForWriting, // Can be a race condition.
EDESTADDRREQ => unreachable, // `connect` was never called.
EDQUOT => return error.DiskQuota,
@@ -2846,8 +2806,8 @@ pub const AcceptError = error{
} || UnexpectedError;
/// Accept a connection on a socket.
-/// If the application has a global event loop enabled, EAGAIN is handled
-/// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
+/// If `sockfd` is opened in non blocking mode, the function will
+/// return error.WouldBlock when EAGAIN is received.
pub fn accept(
/// This argument is a socket that has been created with `socket`, bound to a local address
/// with `bind`, and is listening for connections after a `listen`.
@@ -2890,12 +2850,7 @@ pub fn accept(
return fd;
},
EINTR => continue,
- EAGAIN => if (std.event.Loop.instance) |loop| {
- loop.waitUntilFdReadable(sockfd);
- continue;
- } else {
- return error.WouldBlock;
- },
+ EAGAIN => return error.WouldBlock,
EBADF => unreachable, // always a race condition
ECONNABORTED => return error.ConnectionAborted,
EFAULT => unreachable,
@@ -3081,6 +3036,8 @@ pub const ConnectError = error{
} || UnexpectedError;
/// Initiate a connection on a socket.
+/// If `sockfd` is opened in non blocking mode, the function will
+/// return error.WouldBlock when EAGAIN or EINPROGRESS is received.
pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void {
if (builtin.os.tag == .windows) {
const rc = windows.ws2_32.connect(sockfd, sock_addr, len);
@@ -3113,11 +3070,7 @@ pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) Con
EADDRINUSE => return error.AddressInUse,
EADDRNOTAVAIL => return error.AddressNotAvailable,
EAFNOSUPPORT => return error.AddressFamilyNotSupported,
- EAGAIN, EINPROGRESS => {
- const loop = std.event.Loop.instance orelse return error.WouldBlock;
- loop.waitUntilFdWritable(sockfd);
- return getsockoptError(sockfd);
- },
+ EAGAIN, EINPROGRESS => return error.WouldBlock,
EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
EBADF => unreachable, // sockfd is not a valid open file descriptor.
ECONNREFUSED => return error.ConnectionRefused,
@@ -4620,14 +4573,8 @@ pub fn sendto(
const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen);
switch (errno(rc)) {
0 => return @intCast(usize, rc),
-
EACCES => return error.AccessDenied,
- EAGAIN => if (std.event.Loop.instance) |loop| {
- loop.waitUntilFdWritable(sockfd);
- continue;
- } else {
- return error.WouldBlock;
- },
+ EAGAIN => return error.WouldBlock,
EALREADY => return error.FastOpenAlreadyInProgress,
EBADF => unreachable, // always a race condition
ECONNRESET => return error.ConnectionResetByPeer,
@@ -5106,6 +5053,8 @@ pub const RecvFromError = error{
SystemResources,
} || UnexpectedError;
+/// If `sockfd` is opened in non blocking mode, the function will
+/// return error.WouldBlock when EAGAIN is received.
pub fn recvfrom(
sockfd: fd_t,
buf: []u8,
@@ -5123,12 +5072,7 @@ pub fn recvfrom(
ENOTCONN => unreachable,
ENOTSOCK => unreachable,
EINTR => continue,
- EAGAIN => if (std.event.Loop.instance) |loop| {
- loop.waitUntilFdReadable(sockfd);
- continue;
- } else {
- return error.WouldBlock;
- },
+ EAGAIN => return error.WouldBlock,
ENOMEM => return error.SystemResources,
ECONNREFUSED => return error.ConnectionRefused,
else => |err| return unexpectedErrno(err),
diff --git a/lib/std/os/uefi/tables/system_table.zig b/lib/std/os/uefi/tables/system_table.zig
index cbe66fbb68..3f0624d2ce 100644
--- a/lib/std/os/uefi/tables/system_table.zig
+++ b/lib/std/os/uefi/tables/system_table.zig
@@ -35,7 +35,7 @@ pub const SystemTable = extern struct {
runtime_services: *RuntimeServices,
boot_services: ?*BootServices,
number_of_table_entries: usize,
- configuration_table: *ConfigurationTable,
+ configuration_table: [*]ConfigurationTable,
pub const signature: u64 = 0x5453595320494249;
pub const revision_1_02: u32 = (1 << 16) | 2;
diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig
index de0d0ea45f..fab202bf79 100644
--- a/lib/std/os/windows.zig
+++ b/lib/std/os/windows.zig
@@ -217,6 +217,7 @@ pub fn DeviceIoControl(
switch (rc) {
.SUCCESS => {},
.PRIVILEGE_NOT_HELD => return error.AccessDenied,
+ .ACCESS_DENIED => return error.AccessDenied,
.INVALID_PARAMETER => unreachable,
else => return unexpectedStatus(rc),
}
@@ -760,6 +761,7 @@ pub const DeleteFileError = error{
FileNotFound,
AccessDenied,
NameTooLong,
+ /// Also known as sharing violation.
FileBusy,
Unexpected,
NotDir,
@@ -824,6 +826,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
.INVALID_PARAMETER => unreachable,
.FILE_IS_A_DIRECTORY => return error.IsDir,
.NOT_A_DIRECTORY => return error.NotDir,
+ .SHARING_VIOLATION => return error.FileBusy,
else => return unexpectedStatus(rc),
}
}
diff --git a/lib/std/rb.zig b/lib/std/rb.zig
deleted file mode 100644
index 8cf90a1eea..0000000000
--- a/lib/std/rb.zig
+++ /dev/null
@@ -1,633 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 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 assert = std.debug.assert;
-const testing = std.testing;
-const Order = std.math.Order;
-
-const Color = enum(u1) {
- Black,
- Red,
-};
-const Red = Color.Red;
-const Black = Color.Black;
-
-const ReplaceError = error{NotEqual};
-const SortError = error{NotUnique}; // The new comparison function results in duplicates.
-
-/// Insert this into your struct that you want to add to a red-black tree.
-/// Do not use a pointer. Turn the *rb.Node results of the functions in rb
-/// (after resolving optionals) to your structure using @fieldParentPtr(). Example:
-///
-/// const Number = struct {
-/// node: rb.Node,
-/// value: i32,
-/// };
-/// fn number(node: *rb.Node) Number {
-/// return @fieldParentPtr(Number, "node", node);
-/// }
-pub const Node = struct {
- left: ?*Node,
- right: ?*Node,
-
- /// parent | color
- parent_and_color: usize,
-
- pub fn next(constnode: *Node) ?*Node {
- var node = constnode;
-
- if (node.right) |right| {
- var n = right;
- while (n.left) |left|
- n = left;
- return n;
- }
-
- while (true) {
- var parent = node.getParent();
- if (parent) |p| {
- if (node != p.right)
- return p;
- node = p;
- } else
- return null;
- }
- }
-
- pub fn prev(constnode: *Node) ?*Node {
- var node = constnode;
-
- if (node.left) |left| {
- var n = left;
- while (n.right) |right|
- n = right;
- return n;
- }
-
- while (true) {
- var parent = node.getParent();
- if (parent) |p| {
- if (node != p.left)
- return p;
- node = p;
- } else
- return null;
- }
- }
-
- pub fn isRoot(node: *Node) bool {
- return node.getParent() == null;
- }
-
- fn isRed(node: *Node) bool {
- return node.getColor() == Red;
- }
-
- fn isBlack(node: *Node) bool {
- return node.getColor() == Black;
- }
-
- fn setParent(node: *Node, parent: ?*Node) void {
- node.parent_and_color = @ptrToInt(parent) | (node.parent_and_color & 1);
- }
-
- fn getParent(node: *Node) ?*Node {
- const mask: usize = 1;
- comptime {
- assert(@alignOf(*Node) >= 2);
- }
- const maybe_ptr = node.parent_and_color & ~mask;
- return if (maybe_ptr == 0) null else @intToPtr(*Node, maybe_ptr);
- }
-
- fn setColor(node: *Node, color: Color) void {
- const mask: usize = 1;
- node.parent_and_color = (node.parent_and_color & ~mask) | @enumToInt(color);
- }
-
- fn getColor(node: *Node) Color {
- return @intToEnum(Color, @intCast(u1, node.parent_and_color & 1));
- }
-
- fn setChild(node: *Node, child: ?*Node, is_left: bool) void {
- if (is_left) {
- node.left = child;
- } else {
- node.right = child;
- }
- }
-
- fn getFirst(nodeconst: *Node) *Node {
- var node = nodeconst;
- while (node.left) |left| {
- node = left;
- }
- return node;
- }
-
- fn getLast(nodeconst: *Node) *Node {
- var node = nodeconst;
- while (node.right) |right| {
- node = right;
- }
- return node;
- }
-};
-
-pub const Tree = struct {
- root: ?*Node,
- compareFn: fn (*Node, *Node, *Tree) Order,
-
- /// Re-sorts a tree with a new compare function
- pub fn sort(tree: *Tree, newCompareFn: fn (*Node, *Node, *Tree) Order) SortError!void {
- var newTree = Tree.init(newCompareFn);
- var node: *Node = undefined;
- while (true) {
- node = tree.first() orelse break;
- tree.remove(node);
- if (newTree.insert(node) != null) {
- return error.NotUnique; // EEXISTS
- }
- }
- tree.* = newTree;
- }
-
- /// If you have a need for a version that caches this, please file a bug.
- pub fn first(tree: *Tree) ?*Node {
- var node: *Node = tree.root orelse return null;
-
- while (node.left) |left| {
- node = left;
- }
-
- return node;
- }
-
- pub fn last(tree: *Tree) ?*Node {
- var node: *Node = tree.root orelse return null;
-
- while (node.right) |right| {
- node = right;
- }
-
- return node;
- }
-
- /// Duplicate keys are not allowed. The item with the same key already in the
- /// tree will be returned, and the item will not be inserted.
- pub fn insert(tree: *Tree, node_const: *Node) ?*Node {
- var node = node_const;
- var maybe_key: ?*Node = undefined;
- var maybe_parent: ?*Node = undefined;
- var is_left: bool = undefined;
-
- maybe_key = doLookup(node, tree, &maybe_parent, &is_left);
- if (maybe_key) |key| {
- return key;
- }
-
- node.left = null;
- node.right = null;
- node.setColor(Red);
- node.setParent(maybe_parent);
-
- if (maybe_parent) |parent| {
- parent.setChild(node, is_left);
- } else {
- tree.root = node;
- }
-
- while (node.getParent()) |*parent| {
- if (parent.*.isBlack())
- break;
- // the root is always black
- var grandpa = parent.*.getParent() orelse unreachable;
-
- if (parent.* == grandpa.left) {
- var maybe_uncle = grandpa.right;
-
- if (maybe_uncle) |uncle| {
- if (uncle.isBlack())
- break;
-
- parent.*.setColor(Black);
- uncle.setColor(Black);
- grandpa.setColor(Red);
- node = grandpa;
- } else {
- if (node == parent.*.right) {
- rotateLeft(parent.*, tree);
- node = parent.*;
- parent.* = node.getParent().?; // Just rotated
- }
- parent.*.setColor(Black);
- grandpa.setColor(Red);
- rotateRight(grandpa, tree);
- }
- } else {
- var maybe_uncle = grandpa.left;
-
- if (maybe_uncle) |uncle| {
- if (uncle.isBlack())
- break;
-
- parent.*.setColor(Black);
- uncle.setColor(Black);
- grandpa.setColor(Red);
- node = grandpa;
- } else {
- if (node == parent.*.left) {
- rotateRight(parent.*, tree);
- node = parent.*;
- parent.* = node.getParent().?; // Just rotated
- }
- parent.*.setColor(Black);
- grandpa.setColor(Red);
- rotateLeft(grandpa, tree);
- }
- }
- }
- // This was an insert, there is at least one node.
- tree.root.?.setColor(Black);
- return null;
- }
-
- /// lookup searches for the value of key, using binary search. It will
- /// return a pointer to the node if it is there, otherwise it will return null.
- /// Complexity guaranteed O(log n), where n is the number of nodes book-kept
- /// by tree.
- pub fn lookup(tree: *Tree, key: *Node) ?*Node {
- var parent: ?*Node = undefined;
- var is_left: bool = undefined;
- return doLookup(key, tree, &parent, &is_left);
- }
-
- /// If node is not part of tree, behavior is undefined.
- pub fn remove(tree: *Tree, nodeconst: *Node) void {
- var node = nodeconst;
- // as this has the same value as node, it is unsafe to access node after newnode
- var newnode: ?*Node = nodeconst;
- var maybe_parent: ?*Node = node.getParent();
- var color: Color = undefined;
- var next: *Node = undefined;
-
- // This clause is to avoid optionals
- if (node.left == null and node.right == null) {
- if (maybe_parent) |parent| {
- parent.setChild(null, parent.left == node);
- } else
- tree.root = null;
- color = node.getColor();
- newnode = null;
- } else {
- if (node.left == null) {
- next = node.right.?; // Not both null as per above
- } else if (node.right == null) {
- next = node.left.?; // Not both null as per above
- } else
- next = node.right.?.getFirst(); // Just checked for null above
-
- if (maybe_parent) |parent| {
- parent.setChild(next, parent.left == node);
- } else
- tree.root = next;
-
- if (node.left != null and node.right != null) {
- const left = node.left.?;
- const right = node.right.?;
-
- color = next.getColor();
- next.setColor(node.getColor());
-
- next.left = left;
- left.setParent(next);
-
- if (next != right) {
- var parent = next.getParent().?; // Was traversed via child node (right/left)
- next.setParent(node.getParent());
-
- newnode = next.right;
- parent.left = node;
-
- next.right = right;
- right.setParent(next);
- } else {
- next.setParent(maybe_parent);
- maybe_parent = next;
- newnode = next.right;
- }
- } else {
- color = node.getColor();
- newnode = next;
- }
- }
-
- if (newnode) |n|
- n.setParent(maybe_parent);
-
- if (color == Red)
- return;
- if (newnode) |n| {
- n.setColor(Black);
- return;
- }
-
- while (node == tree.root) {
- // If not root, there must be parent
- var parent = maybe_parent.?;
- if (node == parent.left) {
- var sibling = parent.right.?; // Same number of black nodes.
-
- if (sibling.isRed()) {
- sibling.setColor(Black);
- parent.setColor(Red);
- rotateLeft(parent, tree);
- sibling = parent.right.?; // Just rotated
- }
- if ((if (sibling.left) |n| n.isBlack() else true) and
- (if (sibling.right) |n| n.isBlack() else true))
- {
- sibling.setColor(Red);
- node = parent;
- maybe_parent = parent.getParent();
- continue;
- }
- if (if (sibling.right) |n| n.isBlack() else true) {
- sibling.left.?.setColor(Black); // Same number of black nodes.
- sibling.setColor(Red);
- rotateRight(sibling, tree);
- sibling = parent.right.?; // Just rotated
- }
- sibling.setColor(parent.getColor());
- parent.setColor(Black);
- sibling.right.?.setColor(Black); // Same number of black nodes.
- rotateLeft(parent, tree);
- newnode = tree.root;
- break;
- } else {
- var sibling = parent.left.?; // Same number of black nodes.
-
- if (sibling.isRed()) {
- sibling.setColor(Black);
- parent.setColor(Red);
- rotateRight(parent, tree);
- sibling = parent.left.?; // Just rotated
- }
- if ((if (sibling.left) |n| n.isBlack() else true) and
- (if (sibling.right) |n| n.isBlack() else true))
- {
- sibling.setColor(Red);
- node = parent;
- maybe_parent = parent.getParent();
- continue;
- }
- if (if (sibling.left) |n| n.isBlack() else true) {
- sibling.right.?.setColor(Black); // Same number of black nodes
- sibling.setColor(Red);
- rotateLeft(sibling, tree);
- sibling = parent.left.?; // Just rotated
- }
- sibling.setColor(parent.getColor());
- parent.setColor(Black);
- sibling.left.?.setColor(Black); // Same number of black nodes
- rotateRight(parent, tree);
- newnode = tree.root;
- break;
- }
-
- if (node.isRed())
- break;
- }
-
- if (newnode) |n|
- n.setColor(Black);
- }
-
- /// This is a shortcut to avoid removing and re-inserting an item with the same key.
- pub fn replace(tree: *Tree, old: *Node, newconst: *Node) !void {
- var new = newconst;
-
- // I assume this can get optimized out if the caller already knows.
- if (tree.compareFn(old, new, tree) != .eq) return ReplaceError.NotEqual;
-
- if (old.getParent()) |parent| {
- parent.setChild(new, parent.left == old);
- } else
- tree.root = new;
-
- if (old.left) |left|
- left.setParent(new);
- if (old.right) |right|
- right.setParent(new);
-
- new.* = old.*;
- }
-
- pub fn init(f: fn (*Node, *Node, *Tree) Order) Tree {
- return Tree{
- .root = null,
- .compareFn = f,
- };
- }
-};
-
-fn rotateLeft(node: *Node, tree: *Tree) void {
- var p: *Node = node;
- var q: *Node = node.right orelse unreachable;
- var parent: *Node = undefined;
-
- if (!p.isRoot()) {
- parent = p.getParent().?;
- if (parent.left == p) {
- parent.left = q;
- } else {
- parent.right = q;
- }
- q.setParent(parent);
- } else {
- tree.root = q;
- q.setParent(null);
- }
- p.setParent(q);
-
- p.right = q.left;
- if (p.right) |right| {
- right.setParent(p);
- }
- q.left = p;
-}
-
-fn rotateRight(node: *Node, tree: *Tree) void {
- var p: *Node = node;
- var q: *Node = node.left orelse unreachable;
- var parent: *Node = undefined;
-
- if (!p.isRoot()) {
- parent = p.getParent().?;
- if (parent.left == p) {
- parent.left = q;
- } else {
- parent.right = q;
- }
- q.setParent(parent);
- } else {
- tree.root = q;
- q.setParent(null);
- }
- p.setParent(q);
-
- p.left = q.right;
- if (p.left) |left| {
- left.setParent(p);
- }
- q.right = p;
-}
-
-fn doLookup(key: *Node, tree: *Tree, pparent: *?*Node, is_left: *bool) ?*Node {
- var maybe_node: ?*Node = tree.root;
-
- pparent.* = null;
- is_left.* = false;
-
- while (maybe_node) |node| {
- const res = tree.compareFn(node, key, tree);
- if (res == .eq) {
- return node;
- }
- pparent.* = node;
- switch (res) {
- .gt => {
- is_left.* = true;
- maybe_node = node.left;
- },
- .lt => {
- is_left.* = false;
- maybe_node = node.right;
- },
- .eq => unreachable, // handled above
- }
- }
- return null;
-}
-
-const testNumber = struct {
- node: Node,
- value: usize,
-};
-
-fn testGetNumber(node: *Node) *testNumber {
- return @fieldParentPtr(testNumber, "node", node);
-}
-
-fn testCompare(l: *Node, r: *Node, contextIgnored: *Tree) Order {
- var left = testGetNumber(l);
- var right = testGetNumber(r);
-
- if (left.value < right.value) {
- return .lt;
- } else if (left.value == right.value) {
- return .eq;
- } else if (left.value > right.value) {
- return .gt;
- }
- unreachable;
-}
-
-fn testCompareReverse(l: *Node, r: *Node, contextIgnored: *Tree) Order {
- return testCompare(r, l, contextIgnored);
-}
-
-test "rb" {
- if (@import("builtin").arch == .aarch64) {
- // TODO https://github.com/ziglang/zig/issues/3288
- return error.SkipZigTest;
- }
-
- var tree = Tree.init(testCompare);
- var ns: [10]testNumber = undefined;
- ns[0].value = 42;
- ns[1].value = 41;
- ns[2].value = 40;
- ns[3].value = 39;
- ns[4].value = 38;
- ns[5].value = 39;
- ns[6].value = 3453;
- ns[7].value = 32345;
- ns[8].value = 392345;
- ns[9].value = 4;
-
- var dup: testNumber = undefined;
- dup.value = 32345;
-
- _ = tree.insert(&ns[1].node);
- _ = tree.insert(&ns[2].node);
- _ = tree.insert(&ns[3].node);
- _ = tree.insert(&ns[4].node);
- _ = tree.insert(&ns[5].node);
- _ = tree.insert(&ns[6].node);
- _ = tree.insert(&ns[7].node);
- _ = tree.insert(&ns[8].node);
- _ = tree.insert(&ns[9].node);
- tree.remove(&ns[3].node);
- testing.expect(tree.insert(&dup.node) == &ns[7].node);
- try tree.replace(&ns[7].node, &dup.node);
-
- var num: *testNumber = undefined;
- num = testGetNumber(tree.first().?);
- while (num.node.next() != null) {
- testing.expect(testGetNumber(num.node.next().?).value > num.value);
- num = testGetNumber(num.node.next().?);
- }
-}
-
-test "inserting and looking up" {
- var tree = Tree.init(testCompare);
- var number: testNumber = undefined;
- number.value = 1000;
- _ = tree.insert(&number.node);
- var dup: testNumber = undefined;
- //Assert that tuples with identical value fields finds the same pointer
- dup.value = 1000;
- assert(tree.lookup(&dup.node) == &number.node);
- //Assert that tuples with identical values do not clobber when inserted.
- _ = tree.insert(&dup.node);
- assert(tree.lookup(&dup.node) == &number.node);
- assert(tree.lookup(&number.node) != &dup.node);
- assert(testGetNumber(tree.lookup(&dup.node).?).value == testGetNumber(&dup.node).value);
- //Assert that if looking for a non-existing value, return null.
- var non_existing_value: testNumber = undefined;
- non_existing_value.value = 1234;
- assert(tree.lookup(&non_existing_value.node) == null);
-}
-
-test "multiple inserts, followed by calling first and last" {
- if (@import("builtin").arch == .aarch64) {
- // TODO https://github.com/ziglang/zig/issues/3288
- return error.SkipZigTest;
- }
- var tree = Tree.init(testCompare);
- var zeroth: testNumber = undefined;
- zeroth.value = 0;
- var first: testNumber = undefined;
- first.value = 1;
- var second: testNumber = undefined;
- second.value = 2;
- var third: testNumber = undefined;
- third.value = 3;
- _ = tree.insert(&zeroth.node);
- _ = tree.insert(&first.node);
- _ = tree.insert(&second.node);
- _ = tree.insert(&third.node);
- assert(testGetNumber(tree.first().?).value == 0);
- assert(testGetNumber(tree.last().?).value == 3);
- var lookupNode: testNumber = undefined;
- lookupNode.value = 3;
- assert(tree.lookup(&lookupNode.node) == &third.node);
- tree.sort(testCompareReverse) catch unreachable;
- assert(testGetNumber(tree.first().?).value == 3);
- assert(testGetNumber(tree.last().?).value == 0);
- assert(tree.lookup(&lookupNode.node) == &third.node);
-}
diff --git a/lib/std/special/build_runner.zig b/lib/std/special/build_runner.zig
index 3ab74a11a2..43d6b96536 100644
--- a/lib/std/special/build_runner.zig
+++ b/lib/std/special/build_runner.zig
@@ -161,16 +161,16 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void
try fmt.allocPrint(allocator, "{} (default)", .{top_level_step.step.name})
else
top_level_step.step.name;
- try out_stream.print(" {s:22} {}\n", .{ name, top_level_step.description });
+ try out_stream.print(" {s:<27} {}\n", .{ name, top_level_step.description });
}
try out_stream.writeAll(
\\
\\General Options:
- \\ --help Print this help and exit
- \\ --verbose Print commands before executing them
- \\ --prefix [path] Override default install prefix
- \\ --search-prefix [path] Add a path to look for binaries, libraries, headers
+ \\ --help Print this help and exit
+ \\ --verbose Print commands before executing them
+ \\ --prefix [path] Override default install prefix
+ \\ --search-prefix [path] Add a path to look for binaries, libraries, headers
\\
\\Project-Specific Options:
\\
@@ -185,7 +185,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void
Builder.typeIdName(option.type_id),
});
defer allocator.free(name);
- try out_stream.print("{s:24} {}\n", .{ name, option.description });
+ try out_stream.print("{s:<29} {}\n", .{ name, option.description });
}
}
diff --git a/lib/std/special/test_runner.zig b/lib/std/special/test_runner.zig
index b9452b79cc..14a35a3aaa 100644
--- a/lib/std/special/test_runner.zig
+++ b/lib/std/special/test_runner.zig
@@ -103,6 +103,6 @@ pub fn log(
log_err_count += 1;
}
if (@enumToInt(message_level) <= @enumToInt(std.testing.log_level)) {
- std.debug.print("[{}] ({}): " ++ format, .{ @tagName(scope), @tagName(message_level) } ++ args);
+ std.debug.print("[{}] ({}): " ++ format ++ "\n", .{ @tagName(scope), @tagName(message_level) } ++ args);
}
}
diff --git a/lib/std/start.zig b/lib/std/start.zig
index aea31a1531..71940b12ca 100644
--- a/lib/std/start.zig
+++ b/lib/std/start.zig
@@ -224,7 +224,7 @@ inline fn initEventLoopAndCallMain() u8 {
if (std.event.Loop.instance) |loop| {
if (!@hasDecl(root, "event_loop")) {
loop.init() catch |err| {
- std.debug.warn("error: {}\n", .{@errorName(err)});
+ std.log.err("{}", .{@errorName(err)});
if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(trace.*);
}
@@ -270,7 +270,7 @@ pub fn callMain() u8 {
},
.ErrorUnion => {
const result = root.main() catch |err| {
- std.debug.warn("error: {}\n", .{@errorName(err)});
+ std.log.err("{}", .{@errorName(err)});
if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(trace.*);
}
diff --git a/lib/std/std.zig b/lib/std/std.zig
index 4236b29298..62f7f21f4e 100644
--- a/lib/std/std.zig
+++ b/lib/std/std.zig
@@ -14,7 +14,6 @@ pub const AutoArrayHashMap = array_hash_map.AutoArrayHashMap;
pub const AutoArrayHashMapUnmanaged = array_hash_map.AutoArrayHashMapUnmanaged;
pub const AutoHashMap = hash_map.AutoHashMap;
pub const AutoHashMapUnmanaged = hash_map.AutoHashMapUnmanaged;
-pub const BloomFilter = @import("bloom_filter.zig").BloomFilter;
pub const BufMap = @import("buf_map.zig").BufMap;
pub const BufSet = @import("buf_set.zig").BufSet;
pub const ChildProcess = @import("child_process.zig").ChildProcess;
@@ -48,7 +47,6 @@ pub const base64 = @import("base64.zig");
pub const build = @import("build.zig");
pub const builtin = @import("builtin.zig");
pub const c = @import("c.zig");
-pub const cache_hash = @import("cache_hash.zig");
pub const coff = @import("coff.zig");
pub const compress = @import("compress.zig");
pub const crypto = @import("crypto.zig");
@@ -63,7 +61,6 @@ pub const fs = @import("fs.zig");
pub const hash = @import("hash.zig");
pub const hash_map = @import("hash_map.zig");
pub const heap = @import("heap.zig");
-pub const http = @import("http.zig");
pub const io = @import("io.zig");
pub const json = @import("json.zig");
pub const log = @import("log.zig");
@@ -78,7 +75,6 @@ pub const packed_int_array = @import("packed_int_array.zig");
pub const pdb = @import("pdb.zig");
pub const process = @import("process.zig");
pub const rand = @import("rand.zig");
-pub const rb = @import("rb.zig");
pub const sort = @import("sort.zig");
pub const ascii = @import("ascii.zig");
pub const testing = @import("testing.zig");
diff --git a/lib/std/target.zig b/lib/std/target.zig
index 528e9918ee..d39b3874c4 100644
--- a/lib/std/target.zig
+++ b/lib/std/target.zig
@@ -75,6 +75,13 @@ pub const Target = struct {
else => return ".so",
}
}
+
+ pub fn defaultVersionRange(tag: Tag) Os {
+ return .{
+ .tag = tag,
+ .version_range = VersionRange.default(tag),
+ };
+ }
};
/// Based on NTDDI version constants from
@@ -290,11 +297,32 @@ pub const Target = struct {
}
};
- pub fn defaultVersionRange(tag: Tag) Os {
- return .{
- .tag = tag,
- .version_range = VersionRange.default(tag),
- };
+ pub const TaggedVersionRange = union(enum) {
+ none: void,
+ semver: Version.Range,
+ linux: LinuxVersionRange,
+ windows: WindowsVersion.Range,
+ };
+
+ /// Provides a tagged union. `Target` does not store the tag because it is
+ /// redundant with the OS tag; this function abstracts that part away.
+ pub fn getVersionRange(self: Os) TaggedVersionRange {
+ switch (self.tag) {
+ .linux => return TaggedVersionRange{ .linux = self.version_range.linux },
+ .windows => return TaggedVersionRange{ .windows = self.version_range.windows },
+
+ .freebsd,
+ .macosx,
+ .ios,
+ .tvos,
+ .watchos,
+ .netbsd,
+ .openbsd,
+ .dragonfly,
+ => return TaggedVersionRange{ .semver = self.version_range.semver },
+
+ else => return .none,
+ }
}
/// Checks if system is guaranteed to be at least `version` or older than `version`.
@@ -455,18 +483,9 @@ pub const Target = struct {
else => false,
};
}
-
- pub fn oFileExt(abi: Abi) [:0]const u8 {
- return switch (abi) {
- .msvc => ".obj",
- else => ".o",
- };
- }
};
pub const ObjectFormat = enum {
- /// TODO Get rid of this one.
- unknown,
coff,
pe,
elf,
@@ -1116,8 +1135,18 @@ pub const Target = struct {
return linuxTripleSimple(allocator, self.cpu.arch, self.os.tag, self.abi);
}
+ pub fn oFileExt_cpu_arch_abi(cpu_arch: Cpu.Arch, abi: Abi) [:0]const u8 {
+ if (cpu_arch.isWasm()) {
+ return ".o.wasm";
+ }
+ switch (abi) {
+ .msvc => return ".obj",
+ else => return ".o",
+ }
+ }
+
pub fn oFileExt(self: Target) [:0]const u8 {
- return self.abi.oFileExt();
+ return oFileExt_cpu_arch_abi(self.cpu.arch, self.abi);
}
pub fn exeFileExtSimple(cpu_arch: Cpu.Arch, os_tag: Os.Tag) [:0]const u8 {
@@ -1457,6 +1486,27 @@ pub const Target = struct {
=> return result,
}
}
+
+ /// Return whether or not the given host target is capable of executing natively executables
+ /// of the other target.
+ pub fn canExecBinariesOf(host_target: Target, binary_target: Target) bool {
+ if (host_target.os.tag != binary_target.os.tag)
+ return false;
+
+ if (host_target.cpu.arch == binary_target.cpu.arch)
+ return true;
+
+ if (host_target.cpu.arch == .x86_64 and binary_target.cpu.arch == .i386)
+ return true;
+
+ if (host_target.cpu.arch == .aarch64 and binary_target.cpu.arch == .arm)
+ return true;
+
+ if (host_target.cpu.arch == .aarch64_be and binary_target.cpu.arch == .armeb)
+ return true;
+
+ return false;
+ }
};
test "" {
diff --git a/lib/std/testing.zig b/lib/std/testing.zig
index 658d31bb82..ccbd1d6324 100644
--- a/lib/std/testing.zig
+++ b/lib/std/testing.zig
@@ -38,7 +38,7 @@ pub fn expectError(expected_error: anyerror, actual_error_union: anytype) void {
/// This function is intended to be used only in tests. When the two values are not
/// equal, prints diagnostics to stderr to show exactly how they are not equal,
/// then aborts.
-/// The types must match exactly.
+/// `actual` is casted to the type of `expected`.
pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) void {
switch (@typeInfo(@TypeOf(actual))) {
.NoReturn,
diff --git a/lib/std/zig.zig b/lib/std/zig.zig
index 1dedce4067..cc1815e8e5 100644
--- a/lib/std/zig.zig
+++ b/lib/std/zig.zig
@@ -64,24 +64,84 @@ pub fn lineDelta(source: []const u8, start: usize, end: usize) isize {
return line;
}
-/// Returns the standard file system basename of a binary generated by the Zig compiler.
-pub fn binNameAlloc(
- allocator: *std.mem.Allocator,
+pub const BinNameOptions = struct {
root_name: []const u8,
target: std.Target,
output_mode: std.builtin.OutputMode,
- link_mode: ?std.builtin.LinkMode,
-) error{OutOfMemory}![]u8 {
- switch (output_mode) {
- .Exe => return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, target.exeFileExt() }),
- .Lib => {
- const suffix = switch (link_mode orelse .Static) {
- .Static => target.staticLibSuffix(),
- .Dynamic => target.dynamicLibSuffix(),
- };
- return std.fmt.allocPrint(allocator, "{}{}{}", .{ target.libPrefix(), root_name, suffix });
+ link_mode: ?std.builtin.LinkMode = null,
+ object_format: ?std.Target.ObjectFormat = null,
+ version: ?std.builtin.Version = null,
+};
+
+/// Returns the standard file system basename of a binary generated by the Zig compiler.
+pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 {
+ const root_name = options.root_name;
+ const target = options.target;
+ switch (options.object_format orelse target.getObjectFormat()) {
+ .coff, .pe => switch (options.output_mode) {
+ .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }),
+ .Lib => {
+ const suffix = switch (options.link_mode orelse .Static) {
+ .Static => ".lib",
+ .Dynamic => ".dll",
+ };
+ return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, suffix });
+ },
+ .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
},
- .Obj => return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, target.oFileExt() }),
+ .elf => switch (options.output_mode) {
+ .Exe => return allocator.dupe(u8, root_name),
+ .Lib => {
+ switch (options.link_mode orelse .Static) {
+ .Static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
+ target.libPrefix(), root_name,
+ }),
+ .Dynamic => {
+ if (options.version) |ver| {
+ return std.fmt.allocPrint(allocator, "{s}{s}.so.{d}.{d}.{d}", .{
+ target.libPrefix(), root_name, ver.major, ver.minor, ver.patch,
+ });
+ } else {
+ return std.fmt.allocPrint(allocator, "{s}{s}.so", .{
+ target.libPrefix(), root_name,
+ });
+ }
+ },
+ }
+ },
+ .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
+ },
+ .macho => switch (options.output_mode) {
+ .Exe => return allocator.dupe(u8, root_name),
+ .Lib => {
+ switch (options.link_mode orelse .Static) {
+ .Static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
+ target.libPrefix(), root_name,
+ }),
+ .Dynamic => {
+ if (options.version) |ver| {
+ return std.fmt.allocPrint(allocator, "{s}{s}.{d}.{d}.{d}.dylib", .{
+ target.libPrefix(), root_name, ver.major, ver.minor, ver.patch,
+ });
+ } else {
+ return std.fmt.allocPrint(allocator, "{s}{s}.dylib", .{
+ target.libPrefix(), root_name,
+ });
+ }
+ },
+ }
+ return std.fmt.allocPrint(allocator, "{s}{s}{s}", .{ target.libPrefix(), root_name, suffix });
+ },
+ .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
+ },
+ .wasm => switch (options.output_mode) {
+ .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }),
+ .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
+ .Lib => return std.fmt.allocPrint(allocator, "{s}.wasm", .{root_name}),
+ },
+ .c => return std.fmt.allocPrint(allocator, "{s}.c", .{root_name}),
+ .hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}),
+ .raw => return std.fmt.allocPrint(allocator, "{s}.bin", .{root_name}),
}
}
diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig
index 404e8c413a..d8943adde0 100644
--- a/lib/std/zig/ast.zig
+++ b/lib/std/zig/ast.zig
@@ -823,6 +823,15 @@ pub const Node = struct {
}
}
+ pub fn findFirstWithId(self: *Node, id: Id) ?*Node {
+ if (self.id == id) return self;
+ var child_i: usize = 0;
+ while (self.iterate(child_i)) |child| : (child_i += 1) {
+ if (child.findFirstWithId(id)) |result| return result;
+ }
+ return null;
+ }
+
pub fn dump(self: *Node, indent: usize) void {
{
var i: usize = 0;
diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig
index 75fc5969a5..f1ae3457a5 100644
--- a/lib/std/zig/cross_target.zig
+++ b/lib/std/zig/cross_target.zig
@@ -375,7 +375,7 @@ pub const CrossTarget = struct {
// `Target.current.os` works when doing `zig build` because Zig generates a build executable using
// native OS version range. However this will not be accurate otherwise, and
// will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
- var adjusted_os = if (self.os_tag) |os_tag| Target.Os.defaultVersionRange(os_tag) else Target.current.os;
+ var adjusted_os = if (self.os_tag) |os_tag| os_tag.defaultVersionRange() else Target.current.os;
if (self.os_version_min) |min| switch (min) {
.none => {},
@@ -466,7 +466,7 @@ pub const CrossTarget = struct {
}
pub fn oFileExt(self: CrossTarget) [:0]const u8 {
- return self.getAbi().oFileExt();
+ return Target.oFileExt_cpu_arch_abi(self.getCpuArch(), self.getAbi());
}
pub fn exeFileExt(self: CrossTarget) [:0]const u8 {
diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig
index 36ceb400dc..994ad6d5d1 100644
--- a/lib/std/zig/parser_test.zig
+++ b/lib/std/zig/parser_test.zig
@@ -1301,8 +1301,10 @@ test "zig fmt: array literal with hint" {
\\const a = []u8{
\\ 1, 2,
\\ 3, 4,
- \\ 5, 6, // blah
- \\ 7, 8,
+ \\ 5,
+ \\ 6, // blah
+ \\ 7,
+ \\ 8,
\\};
\\const a = []u8{
\\ 1, 2,
@@ -1372,7 +1374,7 @@ test "zig fmt: multiline string parameter in fn call with trailing comma" {
\\ \\ZIG_C_HEADER_FILES {}
\\ \\ZIG_DIA_GUIDS_LIB {}
\\ \\
- \\ ,
+ \\ ,
\\ std.cstr.toSliceConst(c.ZIG_CMAKE_BINARY_DIR),
\\ std.cstr.toSliceConst(c.ZIG_CXX_COMPILER),
\\ std.cstr.toSliceConst(c.ZIG_DIA_GUIDS_LIB),
@@ -3321,6 +3323,326 @@ test "zig fmt: Don't add extra newline after if" {
);
}
+test "zig fmt: comments in ternary ifs" {
+ try testCanonical(
+ \\const x = if (true) {
+ \\ 1;
+ \\} else if (false)
+ \\ // Comment
+ \\ 0;
+ \\const y = if (true)
+ \\ // Comment
+ \\ 1
+ \\else
+ \\ 0;
+ \\
+ \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;
+ \\
+ );
+}
+
+test "zig fmt: test comments in field access chain" {
+ try testCanonical(
+ \\pub const str = struct {
+ \\ pub const Thing = more.more //
+ \\ .more() //
+ \\ .more().more() //
+ \\ .more() //
+ \\ // .more() //
+ \\ .more() //
+ \\ .more();
+ \\ data: Data,
+ \\};
+ \\
+ \\pub const str = struct {
+ \\ pub const Thing = more.more //
+ \\ .more() //
+ \\ // .more() //
+ \\ // .more() //
+ \\ // .more() //
+ \\ .more() //
+ \\ .more();
+ \\ data: Data,
+ \\};
+ \\
+ \\pub const str = struct {
+ \\ pub const Thing = more //
+ \\ .more //
+ \\ .more() //
+ \\ .more();
+ \\ data: Data,
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: Indent comma correctly after multiline string literals in arg list (trailing comma)" {
+ try testCanonical(
+ \\fn foo() void {
+ \\ z.display_message_dialog(
+ \\ *const [323:0]u8,
+ \\ \\Message Text
+ \\ \\------------
+ \\ \\xxxxxxxxxxxx
+ \\ \\xxxxxxxxxxxx
+ \\ ,
+ \\ g.GtkMessageType.GTK_MESSAGE_WARNING,
+ \\ null,
+ \\ );
+ \\
+ \\ z.display_message_dialog(*const [323:0]u8,
+ \\ \\Message Text
+ \\ \\------------
+ \\ \\xxxxxxxxxxxx
+ \\ \\xxxxxxxxxxxx
+ \\ , g.GtkMessageType.GTK_MESSAGE_WARNING, null);
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: Control flow statement as body of blockless if" {
+ try testCanonical(
+ \\pub fn main() void {
+ \\ const zoom_node = if (focused_node == layout_first)
+ \\ if (it.next()) {
+ \\ if (!node.view.pending.float and !node.view.pending.fullscreen) break node;
+ \\ } else null
+ \\ else
+ \\ focused_node;
+ \\
+ \\ const zoom_node = if (focused_node == layout_first) while (it.next()) |node| {
+ \\ if (!node.view.pending.float and !node.view.pending.fullscreen) break node;
+ \\ } else null else
+ \\ focused_node;
+ \\
+ \\ const zoom_node = if (focused_node == layout_first)
+ \\ if (it.next()) {
+ \\ if (!node.view.pending.float and !node.view.pending.fullscreen) break node;
+ \\ } else null;
+ \\
+ \\ const zoom_node = if (focused_node == layout_first) while (it.next()) |node| {
+ \\ if (!node.view.pending.float and !node.view.pending.fullscreen) break node;
+ \\ };
+ \\
+ \\ const zoom_node = if (focused_node == layout_first) for (nodes) |node| {
+ \\ break node;
+ \\ };
+ \\
+ \\ const zoom_node = if (focused_node == layout_first) switch (nodes) {
+ \\ 0 => 0,
+ \\ } else
+ \\ focused_node;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: " {
+ try testCanonical(
+ \\pub fn sendViewTags(self: Self) void {
+ \\ var it = ViewStack(View).iterator(self.output.views.first, std.math.maxInt(u32));
+ \\ while (it.next()) |node|
+ \\ view_tags.append(node.view.current_tags) catch {
+ \\ c.wl_resource_post_no_memory(self.wl_resource);
+ \\ log.crit(.river_status, "out of memory", .{});
+ \\ return;
+ \\ };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: allow trailing line comments to do manual array formatting" {
+ try testCanonical(
+ \\fn foo() void {
+ \\ self.code.appendSliceAssumeCapacity(&[_]u8{
+ \\ 0x55, // push rbp
+ \\ 0x48, 0x89, 0xe5, // mov rbp, rsp
+ \\ 0x48, 0x81, 0xec, // sub rsp, imm32 (with reloc)
+ \\ });
+ \\
+ \\ di_buf.appendAssumeCapacity(&[_]u8{
+ \\ 1, DW.TAG_compile_unit, DW.CHILDREN_no, // header
+ \\ DW.AT_stmt_list, DW_FORM_data4, // form value pairs
+ \\ DW.AT_low_pc, DW_FORM_addr,
+ \\ DW.AT_high_pc, DW_FORM_addr,
+ \\ DW.AT_name, DW_FORM_strp,
+ \\ DW.AT_comp_dir, DW_FORM_strp,
+ \\ DW.AT_producer, DW_FORM_strp,
+ \\ DW.AT_language, DW_FORM_data2,
+ \\ 0, 0, // sentinel
+ \\ });
+ \\
+ \\ self.code.appendSliceAssumeCapacity(&[_]u8{
+ \\ 0x55, // push rbp
+ \\ 0x48, 0x89, 0xe5, // mov rbp, rsp
+ \\ // How do we handle this?
+ \\ //0x48, 0x81, 0xec, // sub rsp, imm32 (with reloc)
+ \\ // Here's a blank line, should that be allowed?
+ \\
+ \\ 0x48, 0x89, 0xe5,
+ \\ 0x33, 0x45,
+ \\ // Now the comment breaks a single line -- how do we handle this?
+ \\ 0x88,
+ \\ });
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: multiline string literals should play nice with array initializers" {
+ try testCanonical(
+ \\fn main() void {
+ \\ var a = .{.{.{.{.{.{.{.{
+ \\ 0,
+ \\ }}}}}}}};
+ \\ myFunc(.{
+ \\ "aaaaaaa", "bbbbbb", "ccccc",
+ \\ "dddd", ("eee"), ("fff"),
+ \\ ("gggg"),
+ \\ // Line comment
+ \\ \\Multiline String Literals can be quite long
+ \\ ,
+ \\ \\Multiline String Literals can be quite long
+ \\ \\Multiline String Literals can be quite long
+ \\ ,
+ \\ \\Multiline String Literals can be quite long
+ \\ \\Multiline String Literals can be quite long
+ \\ \\Multiline String Literals can be quite long
+ \\ \\Multiline String Literals can be quite long
+ \\ ,
+ \\ (
+ \\ \\Multiline String Literals can be quite long
+ \\ ),
+ \\ .{
+ \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+ \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+ \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+ \\ },
+ \\ .{(
+ \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+ \\ )},
+ \\ .{
+ \\ "xxxxxxx", "xxx",
+ \\ (
+ \\ \\ xxx
+ \\ ),
+ \\ "xxx", "xxx",
+ \\ },
+ \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" }, .{ "xxxxxxx", "xxx", "xxx", "xxx" },
+ \\ "aaaaaaa", "bbbbbb", "ccccc", // -
+ \\ "dddd", ("eee"), ("fff"),
+ \\ .{
+ \\ "xxx", "xxx",
+ \\ (
+ \\ \\ xxx
+ \\ ),
+ \\ "xxxxxxxxxxxxxx", "xxx",
+ \\ },
+ \\ .{
+ \\ (
+ \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+ \\ ),
+ \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+ \\ },
+ \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+ \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+ \\ });
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: use of comments and Multiline string literals may force the parameters over multiple lines" {
+ try testCanonical(
+ \\pub fn makeMemUndefined(qzz: []u8) i1 {
+ \\ cases.add( // fixed bug #2032
+ \\ "compile diagnostic string for top level decl type",
+ \\ \\export fn entry() void {
+ \\ \\ var foo: u32 = @This(){};
+ \\ \\}
+ \\ , &[_][]const u8{
+ \\ "tmp.zig:2:27: error: type 'u32' does not support array initialization",
+ \\ });
+ \\ @compileError(
+ \\ \\ unknown-length pointers and C pointers cannot be hashed deeply.
+ \\ \\ Consider providing your own hash function.
+ \\ \\ unknown-length pointers and C pointers cannot be hashed deeply.
+ \\ \\ Consider providing your own hash function.
+ \\ );
+ \\ return @intCast(i1, doMemCheckClientRequestExpr(0, // default return
+ \\ .MakeMemUndefined, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0));
+ \\}
+ \\
+ \\// This looks like garbage don't do this
+ \\const rparen = tree.prevToken(
+ \\// the first token for the annotation expressions is the left
+ \\// parenthesis, hence the need for two prevToken
+ \\ if (fn_proto.getAlignExpr()) |align_expr|
+ \\ tree.prevToken(tree.prevToken(align_expr.firstToken()))
+ \\else if (fn_proto.getSectionExpr()) |section_expr|
+ \\ tree.prevToken(tree.prevToken(section_expr.firstToken()))
+ \\else if (fn_proto.getCallconvExpr()) |callconv_expr|
+ \\ tree.prevToken(tree.prevToken(callconv_expr.firstToken()))
+ \\else switch (fn_proto.return_type) {
+ \\ .Explicit => |node| node.firstToken(),
+ \\ .InferErrorSet => |node| tree.prevToken(node.firstToken()),
+ \\ .Invalid => unreachable,
+ \\});
+ \\
+ );
+}
+
+test "zig fmt: single argument trailing commas in @builtins()" {
+ try testCanonical(
+ \\pub fn foo(qzz: []u8) i1 {
+ \\ @panic(
+ \\ foo,
+ \\ );
+ \\ panic(
+ \\ foo,
+ \\ );
+ \\ @panic(
+ \\ foo,
+ \\ bar,
+ \\ );
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: trailing comma should force multiline 1 column" {
+ try testTransform(
+ \\pub const UUID_NULL: uuid_t = [16]u8{0,0,0,0,};
+ \\
+ ,
+ \\pub const UUID_NULL: uuid_t = [16]u8{
+ \\ 0,
+ \\ 0,
+ \\ 0,
+ \\ 0,
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: function params should align nicely" {
+ try testCanonical(
+ \\pub fn foo() void {
+ \\ cases.addRuntimeSafety("slicing operator with sentinel",
+ \\ \\const std = @import("std");
+ \\ ++ check_panic_msg ++
+ \\ \\pub fn main() void {
+ \\ \\ var buf = [4]u8{'a','b','c',0};
+ \\ \\ const slice = buf[0..:0];
+ \\ \\}
+ \\ );
+ \\}
+ \\
+ );
+}
+
const std = @import("std");
const mem = std.mem;
const warn = std.debug.warn;
diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig
index 237ca07d2b..67afbb77d9 100644
--- a/lib/std/zig/render.zig
+++ b/lib/std/zig/render.zig
@@ -522,7 +522,11 @@ fn renderExpression(
break :blk if (loc.line == 0) op_space else Space.Newline;
};
- try renderToken(tree, ais, infix_op_node.op_token, after_op_space);
+ {
+ ais.pushIndent();
+ defer ais.popIndent();
+ try renderToken(tree, ais, infix_op_node.op_token, after_op_space);
+ }
ais.pushIndentOneShot();
return renderExpression(allocator, ais, tree, infix_op_node.rhs, space);
},
@@ -710,141 +714,194 @@ fn renderExpression(
.node => |node| tree.nextToken(node.lastToken()),
};
- if (exprs.len == 0) {
- switch (lhs) {
- .dot => |dot| try renderToken(tree, ais, dot, Space.None),
- .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
- }
-
- {
- ais.pushIndent();
- defer ais.popIndent();
- try renderToken(tree, ais, lbrace, Space.None);
- }
-
- return renderToken(tree, ais, rtoken, space);
- }
- if (exprs.len == 1 and tree.token_ids[exprs[0].*.lastToken() + 1] == .RBrace) {
- const expr = exprs[0];
-
- switch (lhs) {
- .dot => |dot| try renderToken(tree, ais, dot, Space.None),
- .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
- }
- try renderToken(tree, ais, lbrace, Space.None);
- try renderExpression(allocator, ais, tree, expr, Space.None);
- return renderToken(tree, ais, rtoken, space);
- }
-
switch (lhs) {
.dot => |dot| try renderToken(tree, ais, dot, Space.None),
.node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
}
+ if (exprs.len == 0) {
+ try renderToken(tree, ais, lbrace, Space.None);
+ return renderToken(tree, ais, rtoken, space);
+ }
+
+ if (exprs.len == 1 and exprs[0].tag != .MultilineStringLiteral and tree.token_ids[exprs[0].*.lastToken() + 1] == .RBrace) {
+ const expr = exprs[0];
+
+ try renderToken(tree, ais, lbrace, Space.None);
+ try renderExpression(allocator, ais, tree, expr, Space.None);
+ return renderToken(tree, ais, rtoken, space);
+ }
+
// scan to find row size
- const maybe_row_size: ?usize = blk: {
- var count: usize = 1;
- for (exprs) |expr, i| {
- if (i + 1 < exprs.len) {
- const expr_last_token = expr.lastToken() + 1;
- const loc = tree.tokenLocation(tree.token_locs[expr_last_token].end, exprs[i + 1].firstToken());
- if (loc.line != 0) break :blk count;
- count += 1;
- } else {
- const expr_last_token = expr.lastToken();
- const loc = tree.tokenLocation(tree.token_locs[expr_last_token].end, rtoken);
- if (loc.line == 0) {
- // all on one line
- const src_has_trailing_comma = trailblk: {
- const maybe_comma = tree.prevToken(rtoken);
- break :trailblk tree.token_ids[maybe_comma] == .Comma;
- };
- if (src_has_trailing_comma) {
- break :blk 1; // force row size 1
- } else {
- break :blk null; // no newlines
- }
- }
- break :blk count;
- }
- }
- unreachable;
- };
-
- if (maybe_row_size) |row_size| {
- // A place to store the width of each expression and its column's maximum
- var widths = try allocator.alloc(usize, exprs.len + row_size);
- defer allocator.free(widths);
- mem.set(usize, widths, 0);
-
- var expr_widths = widths[0 .. widths.len - row_size];
- var column_widths = widths[widths.len - row_size ..];
-
- // Null ais for counting the printed length of each expression
- var counting_stream = std.io.countingOutStream(std.io.null_out_stream);
- var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, counting_stream.writer());
-
- for (exprs) |expr, i| {
- counting_stream.bytes_written = 0;
- try renderExpression(allocator, &auto_indenting_stream, tree, expr, Space.None);
- const width = @intCast(usize, counting_stream.bytes_written);
- const col = i % row_size;
- column_widths[col] = std.math.max(column_widths[col], width);
- expr_widths[i] = width;
- }
-
+ if (rowSize(tree, exprs, rtoken) != null) {
{
ais.pushIndentNextLine();
defer ais.popIndent();
try renderToken(tree, ais, lbrace, Space.Newline);
- var col: usize = 1;
- for (exprs) |expr, i| {
- if (i + 1 < exprs.len) {
- const next_expr = exprs[i + 1];
- try renderExpression(allocator, ais, tree, expr, Space.None);
+ var expr_index: usize = 0;
+ while (rowSize(tree, exprs[expr_index..], rtoken)) |row_size| {
+ const row_exprs = exprs[expr_index..];
+ // A place to store the width of each expression and its column's maximum
+ var widths = try allocator.alloc(usize, row_exprs.len + row_size);
+ defer allocator.free(widths);
+ mem.set(usize, widths, 0);
- const comma = tree.nextToken(expr.*.lastToken());
+ var expr_newlines = try allocator.alloc(bool, row_exprs.len);
+ defer allocator.free(expr_newlines);
+ mem.set(bool, expr_newlines, false);
- if (col != row_size) {
- try renderToken(tree, ais, comma, Space.Space); // ,
+ var expr_widths = widths[0 .. widths.len - row_size];
+ var column_widths = widths[widths.len - row_size ..];
- const padding = column_widths[i % row_size] - expr_widths[i];
- try ais.writer().writeByteNTimes(' ', padding);
+ // Find next row with trailing comment (if any) to end the current section
+ var section_end = sec_end: {
+ var this_line_first_expr: usize = 0;
+ var this_line_size = rowSize(tree, row_exprs, rtoken);
+ for (row_exprs) |expr, i| {
+ // Ignore comment on first line of this section
+ if (i == 0 or tree.tokensOnSameLine(row_exprs[0].firstToken(), expr.lastToken())) continue;
+ // Track start of line containing comment
+ if (!tree.tokensOnSameLine(row_exprs[this_line_first_expr].firstToken(), expr.lastToken())) {
+ this_line_first_expr = i;
+ this_line_size = rowSize(tree, row_exprs[this_line_first_expr..], rtoken);
+ }
- col += 1;
- continue;
+ const maybe_comma = expr.lastToken() + 1;
+ const maybe_comment = expr.lastToken() + 2;
+ if (maybe_comment < tree.token_ids.len) {
+ if (tree.token_ids[maybe_comma] == .Comma and
+ tree.token_ids[maybe_comment] == .LineComment and
+ tree.tokensOnSameLine(expr.lastToken(), maybe_comment))
+ {
+ var comment_token_loc = tree.token_locs[maybe_comment];
+ const comment_is_empty = mem.trimRight(u8, tree.tokenSliceLoc(comment_token_loc), " ").len == 2;
+ if (!comment_is_empty) {
+ // Found row ending in comment
+ break :sec_end i - this_line_size.? + 1;
+ }
+ }
+ }
}
- col = 1;
+ break :sec_end row_exprs.len;
+ };
+ expr_index += section_end;
- if (tree.token_ids[tree.nextToken(comma)] != .MultilineStringLiteralLine) {
- try renderToken(tree, ais, comma, Space.Newline); // ,
+ const section_exprs = row_exprs[0..section_end];
+
+ // Null stream for counting the printed length of each expression
+ var line_find_stream = std.io.findByteOutStream('\n', std.io.null_out_stream);
+ var counting_stream = std.io.countingOutStream(line_find_stream.writer());
+ var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, counting_stream.writer());
+
+ // Calculate size of columns in current section
+ var column_counter: usize = 0;
+ var single_line = true;
+ for (section_exprs) |expr, i| {
+ if (i + 1 < section_exprs.len) {
+ counting_stream.bytes_written = 0;
+ line_find_stream.byte_found = false;
+ try renderExpression(allocator, &auto_indenting_stream, tree, expr, Space.None);
+ const width = @intCast(usize, counting_stream.bytes_written);
+ expr_widths[i] = width;
+ expr_newlines[i] = line_find_stream.byte_found;
+
+ if (!line_find_stream.byte_found) {
+ const column = column_counter % row_size;
+ column_widths[column] = std.math.max(column_widths[column], width);
+
+ const expr_last_token = expr.*.lastToken() + 1;
+ const next_expr = section_exprs[i + 1];
+ const loc = tree.tokenLocation(tree.token_locs[expr_last_token].start, next_expr.*.firstToken());
+ if (loc.line == 0) {
+ column_counter += 1;
+ } else {
+ single_line = false;
+ column_counter = 0;
+ }
+ } else {
+ single_line = false;
+ column_counter = 0;
+ }
} else {
- try renderToken(tree, ais, comma, Space.None); // ,
- }
+ counting_stream.bytes_written = 0;
+ try renderExpression(allocator, &auto_indenting_stream, tree, expr, Space.None);
+ const width = @intCast(usize, counting_stream.bytes_written);
+ expr_widths[i] = width;
+ expr_newlines[i] = line_find_stream.byte_found;
- try renderExtraNewline(tree, ais, next_expr);
- } else {
- try renderExpression(allocator, ais, tree, expr, Space.Comma); // ,
+ if (!line_find_stream.byte_found) {
+ const column = column_counter % row_size;
+ column_widths[column] = std.math.max(column_widths[column], width);
+ }
+ break;
+ }
+ }
+
+ // Render exprs in current section
+ column_counter = 0;
+ var last_col_index: usize = row_size - 1;
+ for (section_exprs) |expr, i| {
+ if (i + 1 < section_exprs.len) {
+ const next_expr = section_exprs[i + 1];
+ try renderExpression(allocator, ais, tree, expr, Space.None);
+
+ const comma = tree.nextToken(expr.*.lastToken());
+
+ if (column_counter != last_col_index) {
+ if (!expr_newlines[i] and !expr_newlines[i + 1]) {
+ // Neither the current or next expression is multiline
+ try renderToken(tree, ais, comma, Space.Space); // ,
+ assert(column_widths[column_counter % row_size] >= expr_widths[i]);
+ const padding = column_widths[column_counter % row_size] - expr_widths[i];
+ try ais.writer().writeByteNTimes(' ', padding);
+
+ column_counter += 1;
+ continue;
+ }
+ }
+ if (single_line and row_size != 1) {
+ try renderToken(tree, ais, comma, Space.Space); // ,
+ continue;
+ }
+
+ column_counter = 0;
+ try renderToken(tree, ais, comma, Space.Newline); // ,
+ try renderExtraNewline(tree, ais, next_expr);
+ } else {
+ const maybe_comma = tree.nextToken(expr.*.lastToken());
+ if (tree.token_ids[maybe_comma] == .Comma) {
+ try renderExpression(allocator, ais, tree, expr, Space.None); // ,
+ try renderToken(tree, ais, maybe_comma, Space.Newline); // ,
+ } else {
+ try renderExpression(allocator, ais, tree, expr, Space.Comma); // ,
+ }
+ }
+ }
+
+ if (expr_index == exprs.len) {
+ break;
}
- }
- }
- return renderToken(tree, ais, rtoken, space);
- } else {
- try renderToken(tree, ais, lbrace, Space.Space);
- for (exprs) |expr, i| {
- if (i + 1 < exprs.len) {
- const next_expr = exprs[i + 1];
- try renderExpression(allocator, ais, tree, expr, Space.None);
- const comma = tree.nextToken(expr.*.lastToken());
- try renderToken(tree, ais, comma, Space.Space); // ,
- } else {
- try renderExpression(allocator, ais, tree, expr, Space.Space);
}
}
return renderToken(tree, ais, rtoken, space);
}
+
+ // Single line
+ try renderToken(tree, ais, lbrace, Space.Space);
+ for (exprs) |expr, i| {
+ if (i + 1 < exprs.len) {
+ const next_expr = exprs[i + 1];
+ try renderExpression(allocator, ais, tree, expr, Space.None);
+ const comma = tree.nextToken(expr.*.lastToken());
+ try renderToken(tree, ais, comma, Space.Space); // ,
+ } else {
+ try renderExpression(allocator, ais, tree, expr, Space.Space);
+ }
+ }
+
+ return renderToken(tree, ais, rtoken, space);
},
.StructInitializer, .StructInitializerDot => {
@@ -1004,21 +1061,29 @@ fn renderExpression(
};
if (src_has_trailing_comma) {
- try renderToken(tree, ais, lparen, Space.Newline);
-
- const params = call.params();
- for (params) |param_node, i| {
+ {
ais.pushIndent();
defer ais.popIndent();
- if (i + 1 < params.len) {
- const next_node = params[i + 1];
- try renderExpression(allocator, ais, tree, param_node, Space.None);
- const comma = tree.nextToken(param_node.lastToken());
- try renderToken(tree, ais, comma, Space.Newline); // ,
- try renderExtraNewline(tree, ais, next_node);
- } else {
- try renderExpression(allocator, ais, tree, param_node, Space.Comma);
+ try renderToken(tree, ais, lparen, Space.Newline); // (
+ const params = call.params();
+ for (params) |param_node, i| {
+ if (i + 1 < params.len) {
+ const next_node = params[i + 1];
+ try renderExpression(allocator, ais, tree, param_node, Space.None);
+
+ // Unindent the comma for multiline string literals
+ const maybe_multiline_string = param_node.firstToken();
+ const is_multiline_string = tree.token_ids[maybe_multiline_string] == .MultilineStringLiteralLine;
+ if (is_multiline_string) ais.popIndent();
+ defer if (is_multiline_string) ais.pushIndent();
+
+ const comma = tree.nextToken(param_node.lastToken());
+ try renderToken(tree, ais, comma, Space.Newline); // ,
+ try renderExtraNewline(tree, ais, next_node);
+ } else {
+ try renderExpression(allocator, ais, tree, param_node, Space.Comma);
+ }
}
}
return renderToken(tree, ais, call.rtoken, space);
@@ -1028,17 +1093,20 @@ fn renderExpression(
const params = call.params();
for (params) |param_node, i| {
- if (param_node.*.tag == .MultilineStringLiteral) ais.pushIndentOneShot();
+ const maybe_comment = param_node.firstToken() - 1;
+ const maybe_multiline_string = param_node.firstToken();
+ if (tree.token_ids[maybe_multiline_string] == .MultilineStringLiteralLine or tree.token_ids[maybe_comment] == .LineComment) {
+ ais.pushIndentOneShot();
+ }
try renderExpression(allocator, ais, tree, param_node, Space.None);
if (i + 1 < params.len) {
- const next_param = params[i + 1];
const comma = tree.nextToken(param_node.lastToken());
try renderToken(tree, ais, comma, Space.Space);
}
}
- return renderToken(tree, ais, call.rtoken, space);
+ return renderToken(tree, ais, call.rtoken, space); // )
},
.ArrayAccess => {
@@ -1429,7 +1497,7 @@ fn renderExpression(
try renderToken(tree, ais, builtin_call.builtin_token, Space.None); // @name
const src_params_trailing_comma = blk: {
- if (builtin_call.params_len < 2) break :blk false;
+ if (builtin_call.params_len == 0) break :blk false;
const last_node = builtin_call.params()[builtin_call.params_len - 1];
const maybe_comma = tree.nextToken(last_node.lastToken());
break :blk tree.token_ids[maybe_comma] == .Comma;
@@ -1443,6 +1511,10 @@ fn renderExpression(
// render all on one line, no trailing comma
const params = builtin_call.params();
for (params) |param_node, i| {
+ const maybe_comment = param_node.firstToken() - 1;
+ if (param_node.*.tag == .MultilineStringLiteral or tree.token_ids[maybe_comment] == .LineComment) {
+ ais.pushIndentOneShot();
+ }
try renderExpression(allocator, ais, tree, param_node, Space.None);
if (i + 1 < params.len) {
@@ -1494,19 +1566,20 @@ fn renderExpression(
assert(tree.token_ids[lparen] == .LParen);
const rparen = tree.prevToken(
- // the first token for the annotation expressions is the left
- // parenthesis, hence the need for two prevToken
- if (fn_proto.getAlignExpr()) |align_expr|
- tree.prevToken(tree.prevToken(align_expr.firstToken()))
- else if (fn_proto.getSectionExpr()) |section_expr|
- tree.prevToken(tree.prevToken(section_expr.firstToken()))
- else if (fn_proto.getCallconvExpr()) |callconv_expr|
- tree.prevToken(tree.prevToken(callconv_expr.firstToken()))
- else switch (fn_proto.return_type) {
- .Explicit => |node| node.firstToken(),
- .InferErrorSet => |node| tree.prevToken(node.firstToken()),
- .Invalid => unreachable,
- });
+ // the first token for the annotation expressions is the left
+ // parenthesis, hence the need for two prevToken
+ if (fn_proto.getAlignExpr()) |align_expr|
+ tree.prevToken(tree.prevToken(align_expr.firstToken()))
+ else if (fn_proto.getSectionExpr()) |section_expr|
+ tree.prevToken(tree.prevToken(section_expr.firstToken()))
+ else if (fn_proto.getCallconvExpr()) |callconv_expr|
+ tree.prevToken(tree.prevToken(callconv_expr.firstToken()))
+ else switch (fn_proto.return_type) {
+ .Explicit => |node| node.firstToken(),
+ .InferErrorSet => |node| tree.prevToken(node.firstToken()),
+ .Invalid => unreachable,
+ },
+ );
assert(tree.token_ids[rparen] == .RParen);
const src_params_trailing_comma = blk: {
@@ -1758,7 +1831,7 @@ fn renderExpression(
}
if (while_node.payload) |payload| {
- const payload_space = Space.Space; //if (while_node.continue_expr != null) Space.Space else block_start_space;
+ const payload_space = if (while_node.continue_expr != null) Space.Space else block_start_space;
try renderExpression(allocator, ais, tree, payload, payload_space);
}
@@ -1873,7 +1946,12 @@ fn renderExpression(
if (src_has_newline) {
const after_rparen_space = if (if_node.payload == null) Space.Newline else Space.Space;
- try renderToken(tree, ais, rparen, after_rparen_space); // )
+
+ {
+ ais.pushIndent();
+ defer ais.popIndent();
+ try renderToken(tree, ais, rparen, after_rparen_space); // )
+ }
if (if_node.payload) |payload| {
try renderExpression(allocator, ais, tree, payload, Space.Newline);
@@ -2558,3 +2636,27 @@ fn copyFixingWhitespace(ais: anytype, slice: []const u8) @TypeOf(ais.*).Error!vo
else => try ais.writer().writeByte(byte),
};
}
+
+fn rowSize(tree: *ast.Tree, exprs: []*ast.Node, rtoken: ast.TokenIndex) ?usize {
+ const first_token = exprs[0].firstToken();
+ const first_loc = tree.tokenLocation(tree.token_locs[first_token].start, rtoken);
+ if (first_loc.line == 0) {
+ const maybe_comma = tree.prevToken(rtoken);
+ if (tree.token_ids[maybe_comma] == .Comma)
+ return 1;
+ return null; // no newlines
+ }
+
+ var count: usize = 1;
+ for (exprs) |expr, i| {
+ if (i + 1 < exprs.len) {
+ const expr_last_token = expr.lastToken() + 1;
+ const loc = tree.tokenLocation(tree.token_locs[expr_last_token].start, exprs[i + 1].firstToken());
+ if (loc.line != 0) return count;
+ count += 1;
+ } else {
+ return count;
+ }
+ }
+ unreachable;
+}
diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig
index 37eea36d47..d13d0b22ef 100644
--- a/lib/std/zig/system.zig
+++ b/lib/std/zig/system.zig
@@ -203,7 +203,7 @@ pub const NativeTargetInfo = struct {
/// deinitialization method.
/// TODO Remove the Allocator requirement from this function.
pub fn detect(allocator: *Allocator, cross_target: CrossTarget) DetectError!NativeTargetInfo {
- var os = Target.Os.defaultVersionRange(cross_target.getOsTag());
+ var os = cross_target.getOsTag().defaultVersionRange();
if (cross_target.os_tag == null) {
switch (Target.current.os.tag) {
.linux => {
@@ -393,6 +393,12 @@ pub const NativeTargetInfo = struct {
if (!native_target_has_ld or have_all_info or os_is_non_native) {
return defaultAbiAndDynamicLinker(cpu, os, cross_target);
}
+ if (cross_target.abi) |abi| {
+ if (abi.isMusl()) {
+ // musl implies static linking.
+ return defaultAbiAndDynamicLinker(cpu, os, cross_target);
+ }
+ }
// The current target's ABI cannot be relied on for this. For example, we may build the zig
// compiler for target riscv64-linux-musl and provide a tarball for users to download.
// A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined
diff --git a/lib/std/zig/tokenizer.zig b/lib/std/zig/tokenizer.zig
index 86968c73b2..e40483c022 100644
--- a/lib/std/zig/tokenizer.zig
+++ b/lib/std/zig/tokenizer.zig
@@ -1195,6 +1195,7 @@ pub const Tokenizer = struct {
},
.num_dot_hex => switch (c) {
'.' => {
+ result.id = .IntegerLiteral;
self.index -= 1;
state = .start;
break;
@@ -1758,6 +1759,14 @@ test "correctly parse pointer assignment" {
});
}
+test "tokenizer - range literals" {
+ testTokenize("0...9", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral });
+ testTokenize("'0'...'9'", &[_]Token.Id{ .CharLiteral, .Ellipsis3, .CharLiteral });
+ testTokenize("0x00...0x09", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral });
+ testTokenize("0b00...0b11", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral });
+ testTokenize("0o00...0o11", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral });
+}
+
test "tokenizer - number literals decimal" {
testTokenize("0", &[_]Token.Id{.IntegerLiteral});
testTokenize("1", &[_]Token.Id{.IntegerLiteral});
diff --git a/src-self-hosted/Package.zig b/src-self-hosted/Package.zig
deleted file mode 100644
index 4bf4defbc8..0000000000
--- a/src-self-hosted/Package.zig
+++ /dev/null
@@ -1,59 +0,0 @@
-pub const Table = std.StringHashMap(*Package);
-
-/// This should be used for file operations.
-root_src_dir: std.fs.Dir,
-/// This is for metadata purposes, for example putting into debug information.
-root_src_dir_path: []u8,
-/// Relative to `root_src_dir` and `root_src_dir_path`.
-root_src_path: []u8,
-table: Table,
-
-/// No references to `root_src_dir` and `root_src_path` are kept.
-pub fn create(
- allocator: *mem.Allocator,
- base_dir: std.fs.Dir,
- /// Relative to `base_dir`.
- root_src_dir: []const u8,
- /// Relative to `root_src_dir`.
- root_src_path: []const u8,
-) !*Package {
- const ptr = try allocator.create(Package);
- errdefer allocator.destroy(ptr);
- const root_src_path_dupe = try mem.dupe(allocator, u8, root_src_path);
- errdefer allocator.free(root_src_path_dupe);
- const root_src_dir_path = try mem.dupe(allocator, u8, root_src_dir);
- errdefer allocator.free(root_src_dir_path);
- ptr.* = .{
- .root_src_dir = try base_dir.openDir(root_src_dir, .{}),
- .root_src_dir_path = root_src_dir_path,
- .root_src_path = root_src_path_dupe,
- .table = Table.init(allocator),
- };
- return ptr;
-}
-
-pub fn destroy(self: *Package) void {
- const allocator = self.table.allocator;
- self.root_src_dir.close();
- allocator.free(self.root_src_path);
- allocator.free(self.root_src_dir_path);
- {
- var it = self.table.iterator();
- while (it.next()) |kv| {
- allocator.free(kv.key);
- }
- }
- self.table.deinit();
- allocator.destroy(self);
-}
-
-pub fn add(self: *Package, name: []const u8, package: *Package) !void {
- try self.table.ensureCapacity(self.table.items().len + 1);
- const name_dupe = try mem.dupe(self.table.allocator, u8, name);
- self.table.putAssumeCapacityNoClobber(name_dupe, package);
-}
-
-const std = @import("std");
-const mem = std.mem;
-const assert = std.debug.assert;
-const Package = @This();
diff --git a/src-self-hosted/introspect.zig b/src-self-hosted/introspect.zig
deleted file mode 100644
index 80f10c8656..0000000000
--- a/src-self-hosted/introspect.zig
+++ /dev/null
@@ -1,138 +0,0 @@
-//! Introspection and determination of system libraries needed by zig.
-
-const std = @import("std");
-const mem = std.mem;
-const fs = std.fs;
-const CacheHash = std.cache_hash.CacheHash;
-
-/// Caller must free result
-pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![]u8 {
- {
- const test_zig_dir = try fs.path.join(allocator, &[_][]const u8{ test_path, "lib", "zig" });
- errdefer allocator.free(test_zig_dir);
-
- const test_index_file = try fs.path.join(allocator, &[_][]const u8{ test_zig_dir, "std", "std.zig" });
- defer allocator.free(test_index_file);
-
- if (fs.cwd().openFile(test_index_file, .{})) |file| {
- file.close();
- return test_zig_dir;
- } else |err| switch (err) {
- error.FileNotFound => {
- allocator.free(test_zig_dir);
- },
- else => |e| return e,
- }
- }
-
- // Also try without "zig"
- const test_zig_dir = try fs.path.join(allocator, &[_][]const u8{ test_path, "lib" });
- errdefer allocator.free(test_zig_dir);
-
- const test_index_file = try fs.path.join(allocator, &[_][]const u8{ test_zig_dir, "std", "std.zig" });
- defer allocator.free(test_index_file);
-
- const file = try fs.cwd().openFile(test_index_file, .{});
- file.close();
-
- return test_zig_dir;
-}
-
-/// Caller must free result
-pub fn findZigLibDir(allocator: *mem.Allocator) ![]u8 {
- const self_exe_path = try fs.selfExePathAlloc(allocator);
- defer allocator.free(self_exe_path);
-
- var cur_path: []const u8 = self_exe_path;
- while (true) {
- const test_dir = fs.path.dirname(cur_path) orelse ".";
-
- if (mem.eql(u8, test_dir, cur_path)) {
- break;
- }
-
- return testZigInstallPrefix(allocator, test_dir) catch |err| {
- cur_path = test_dir;
- continue;
- };
- }
-
- return error.FileNotFound;
-}
-
-pub fn resolveZigLibDir(allocator: *mem.Allocator) ![]u8 {
- return findZigLibDir(allocator) catch |err| {
- std.debug.print(
- \\Unable to find zig lib directory: {}.
- \\Reinstall Zig or use --zig-install-prefix.
- \\
- , .{@errorName(err)});
-
- return error.ZigLibDirNotFound;
- };
-}
-
-/// Caller owns returned memory.
-pub fn resolveGlobalCacheDir(allocator: *mem.Allocator) ![]u8 {
- const appname = "zig";
-
- if (std.Target.current.os.tag != .windows) {
- if (std.os.getenv("XDG_CACHE_HOME")) |cache_root| {
- return fs.path.join(allocator, &[_][]const u8{ cache_root, appname });
- } else if (std.os.getenv("HOME")) |home| {
- return fs.path.join(allocator, &[_][]const u8{ home, ".cache", appname });
- }
- }
-
- return fs.getAppDataDir(allocator, appname);
-}
-
-pub fn openGlobalCacheDir() !fs.Dir {
- var buf: [fs.MAX_PATH_BYTES]u8 = undefined;
- var fba = std.heap.FixedBufferAllocator.init(&buf);
- const path_name = try resolveGlobalCacheDir(&fba.allocator);
- return fs.cwd().makeOpenPath(path_name, .{});
-}
-
-var compiler_id_mutex = std.Mutex{};
-var compiler_id: [16]u8 = undefined;
-var compiler_id_computed = false;
-
-pub fn resolveCompilerId(gpa: *mem.Allocator) ![16]u8 {
- const held = compiler_id_mutex.acquire();
- defer held.release();
-
- if (compiler_id_computed)
- return compiler_id;
- compiler_id_computed = true;
-
- var cache_dir = try openGlobalCacheDir();
- defer cache_dir.close();
-
- var ch = try CacheHash.init(gpa, cache_dir, "exe");
- defer ch.release();
-
- const self_exe_path = try fs.selfExePathAlloc(gpa);
- defer gpa.free(self_exe_path);
-
- _ = try ch.addFile(self_exe_path, null);
-
- if (try ch.hit()) |digest| {
- compiler_id = digest[0..16].*;
- return compiler_id;
- }
-
- const libs = try std.process.getSelfExeSharedLibPaths(gpa);
- defer {
- for (libs) |lib| gpa.free(lib);
- gpa.free(libs);
- }
-
- for (libs) |lib| {
- try ch.addFilePost(lib);
- }
-
- const digest = ch.final();
- compiler_id = digest[0..16].*;
- return compiler_id;
-}
diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig
deleted file mode 100644
index fff69a6bbd..0000000000
--- a/src-self-hosted/link.zig
+++ /dev/null
@@ -1,279 +0,0 @@
-const std = @import("std");
-const Allocator = std.mem.Allocator;
-const Module = @import("Module.zig");
-const fs = std.fs;
-const trace = @import("tracy.zig").trace;
-const Package = @import("Package.zig");
-const Type = @import("type.zig").Type;
-const build_options = @import("build_options");
-
-pub const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version;
-
-pub const Options = struct {
- target: std.Target,
- output_mode: std.builtin.OutputMode,
- link_mode: std.builtin.LinkMode,
- object_format: std.builtin.ObjectFormat,
- optimize_mode: std.builtin.Mode,
- root_name: []const u8,
- root_pkg: *const Package,
- /// Used for calculating how much space to reserve for symbols in case the binary file
- /// does not already have a symbol table.
- symbol_count_hint: u64 = 32,
- /// Used for calculating how much space to reserve for executable program code in case
- /// the binary file deos not already have such a section.
- program_code_size_hint: u64 = 256 * 1024,
- entry_addr: ?u64 = null,
-};
-
-pub const File = struct {
- tag: Tag,
- options: Options,
- file: ?fs.File,
- allocator: *Allocator,
-
- pub const LinkBlock = union {
- elf: Elf.TextBlock,
- coff: Coff.TextBlock,
- macho: MachO.TextBlock,
- c: void,
- wasm: void,
- };
-
- pub const LinkFn = union {
- elf: Elf.SrcFn,
- coff: Coff.SrcFn,
- macho: MachO.SrcFn,
- c: void,
- wasm: ?Wasm.FnData,
- };
-
- /// For DWARF .debug_info.
- pub const DbgInfoTypeRelocsTable = std.HashMapUnmanaged(Type, DbgInfoTypeReloc, Type.hash, Type.eql, std.hash_map.DefaultMaxLoadPercentage);
-
- /// For DWARF .debug_info.
- pub const DbgInfoTypeReloc = struct {
- /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl).
- /// This is where the .debug_info tag for the type is.
- off: u32,
- /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl).
- /// List of DW.AT_type / DW.FORM_ref4 that points to the type.
- relocs: std.ArrayListUnmanaged(u32),
- };
-
- /// Attempts incremental linking, if the file already exists. If
- /// incremental linking fails, falls back to truncating the file and
- /// rewriting it. A malicious file is detected as incremental link failure
- /// and does not cause Illegal Behavior. This operation is not atomic.
- pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
- switch (options.object_format) {
- .unknown => unreachable,
- .coff, .pe => return Coff.openPath(allocator, dir, sub_path, options),
- .elf => return Elf.openPath(allocator, dir, sub_path, options),
- .macho => return MachO.openPath(allocator, dir, sub_path, options),
- .wasm => return Wasm.openPath(allocator, dir, sub_path, options),
- .c => return C.openPath(allocator, dir, sub_path, options),
- .hex => return error.TODOImplementHex,
- .raw => return error.TODOImplementRaw,
- }
- }
-
- pub fn cast(base: *File, comptime T: type) ?*T {
- if (base.tag != T.base_tag)
- return null;
-
- return @fieldParentPtr(T, "base", base);
- }
-
- pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {
- switch (base.tag) {
- .coff, .elf, .macho => {
- if (base.file != null) return;
- base.file = try dir.createFile(sub_path, .{
- .truncate = false,
- .read = true,
- .mode = determineMode(base.options),
- });
- },
- .c, .wasm => {},
- }
- }
-
- pub fn makeExecutable(base: *File) !void {
- switch (base.tag) {
- .c => unreachable,
- .wasm => {},
- else => if (base.file) |f| {
- f.close();
- base.file = null;
- },
- }
- }
-
- /// May be called before or after updateDeclExports but must be called
- /// after allocateDeclIndexes for any given Decl.
- pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
- switch (base.tag) {
- .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl),
- .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
- .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),
- .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
- .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl),
- }
- }
-
- pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void {
- switch (base.tag) {
- .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),
- .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
- .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
- .c, .wasm => {},
- }
- }
-
- /// Must be called before any call to updateDecl or updateDeclExports for
- /// any given Decl.
- pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
- switch (base.tag) {
- .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl),
- .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
- .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),
- .c, .wasm => {},
- }
- }
-
- pub fn deinit(base: *File) void {
- if (base.file) |f| f.close();
- switch (base.tag) {
- .coff => @fieldParentPtr(Coff, "base", base).deinit(),
- .elf => @fieldParentPtr(Elf, "base", base).deinit(),
- .macho => @fieldParentPtr(MachO, "base", base).deinit(),
- .c => @fieldParentPtr(C, "base", base).deinit(),
- .wasm => @fieldParentPtr(Wasm, "base", base).deinit(),
- }
- }
-
- pub fn destroy(base: *File) void {
- switch (base.tag) {
- .coff => {
- const parent = @fieldParentPtr(Coff, "base", base);
- parent.deinit();
- base.allocator.destroy(parent);
- },
- .elf => {
- const parent = @fieldParentPtr(Elf, "base", base);
- parent.deinit();
- base.allocator.destroy(parent);
- },
- .macho => {
- const parent = @fieldParentPtr(MachO, "base", base);
- parent.deinit();
- base.allocator.destroy(parent);
- },
- .c => {
- const parent = @fieldParentPtr(C, "base", base);
- parent.deinit();
- base.allocator.destroy(parent);
- },
- .wasm => {
- const parent = @fieldParentPtr(Wasm, "base", base);
- parent.deinit();
- base.allocator.destroy(parent);
- },
- }
- }
-
- pub fn flush(base: *File, module: *Module) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- try switch (base.tag) {
- .coff => @fieldParentPtr(Coff, "base", base).flush(module),
- .elf => @fieldParentPtr(Elf, "base", base).flush(module),
- .macho => @fieldParentPtr(MachO, "base", base).flush(module),
- .c => @fieldParentPtr(C, "base", base).flush(module),
- .wasm => @fieldParentPtr(Wasm, "base", base).flush(module),
- };
- }
-
- pub fn freeDecl(base: *File, decl: *Module.Decl) void {
- switch (base.tag) {
- .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl),
- .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
- .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),
- .c => unreachable,
- .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl),
- }
- }
-
- pub fn errorFlags(base: *File) ErrorFlags {
- return switch (base.tag) {
- .coff => @fieldParentPtr(Coff, "base", base).error_flags,
- .elf => @fieldParentPtr(Elf, "base", base).error_flags,
- .macho => @fieldParentPtr(MachO, "base", base).error_flags,
- .c => return .{ .no_entry_point_found = false },
- .wasm => return ErrorFlags{},
- };
- }
-
- /// May be called before or after updateDecl, but must be called after
- /// allocateDeclIndexes for any given Decl.
- pub fn updateDeclExports(
- base: *File,
- module: *Module,
- decl: *const Module.Decl,
- exports: []const *Module.Export,
- ) !void {
- switch (base.tag) {
- .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports),
- .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
- .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),
- .c => return {},
- .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports),
- }
- }
-
- pub fn getDeclVAddr(base: *File, decl: *const Module.Decl) u64 {
- switch (base.tag) {
- .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl),
- .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl),
- .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl),
- .c => unreachable,
- .wasm => unreachable,
- }
- }
-
- pub const Tag = enum {
- coff,
- elf,
- macho,
- c,
- wasm,
- };
-
- pub const ErrorFlags = struct {
- no_entry_point_found: bool = false,
- };
-
- pub const C = @import("link/C.zig");
- pub const Coff = @import("link/Coff.zig");
- pub const Elf = @import("link/Elf.zig");
- pub const MachO = @import("link/MachO.zig");
- pub const Wasm = @import("link/Wasm.zig");
-};
-
-pub fn determineMode(options: Options) fs.File.Mode {
- // On common systems with a 0o022 umask, 0o777 will still result in a file created
- // with 0o755 permissions, but it works appropriately if the system is configured
- // more leniently. As another data point, C's fopen seems to open files with the
- // 666 mode.
- const executable_mode = if (std.Target.current.os.tag == .windows) 0 else 0o777;
- switch (options.output_mode) {
- .Lib => return switch (options.link_mode) {
- .Dynamic => executable_mode,
- .Static => fs.File.default_mode,
- },
- .Exe => return executable_mode,
- .Obj => return fs.File.default_mode,
- }
-}
diff --git a/src-self-hosted/link/Coff.zig b/src-self-hosted/link/Coff.zig
deleted file mode 100644
index 4d1f95e567..0000000000
--- a/src-self-hosted/link/Coff.zig
+++ /dev/null
@@ -1,792 +0,0 @@
-const Coff = @This();
-
-const std = @import("std");
-const log = std.log.scoped(.link);
-const Allocator = std.mem.Allocator;
-const assert = std.debug.assert;
-const fs = std.fs;
-
-const trace = @import("../tracy.zig").trace;
-const Module = @import("../Module.zig");
-const codegen = @import("../codegen.zig");
-const link = @import("../link.zig");
-
-const allocation_padding = 4 / 3;
-const minimum_text_block_size = 64 * allocation_padding;
-
-const section_alignment = 4096;
-const file_alignment = 512;
-const image_base = 0x400_000;
-const section_table_size = 2 * 40;
-comptime {
- std.debug.assert(std.mem.isAligned(image_base, section_alignment));
-}
-
-pub const base_tag: link.File.Tag = .coff;
-
-const msdos_stub = @embedFile("msdos-stub.bin");
-
-base: link.File,
-ptr_width: enum { p32, p64 },
-error_flags: link.File.ErrorFlags = .{},
-
-text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
-last_text_block: ?*TextBlock = null,
-
-/// Section table file pointer.
-section_table_offset: u32 = 0,
-/// Section data file pointer.
-section_data_offset: u32 = 0,
-/// Optiona header file pointer.
-optional_header_offset: u32 = 0,
-
-/// Absolute virtual address of the offset table when the executable is loaded in memory.
-offset_table_virtual_address: u32 = 0,
-/// Current size of the offset table on disk, must be a multiple of `file_alignment`
-offset_table_size: u32 = 0,
-/// Contains absolute virtual addresses
-offset_table: std.ArrayListUnmanaged(u64) = .{},
-/// Free list of offset table indices
-offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
-
-/// Virtual address of the entry point procedure relative to `image_base`
-entry_addr: ?u32 = null,
-
-/// Absolute virtual address of the text section when the executable is loaded in memory.
-text_section_virtual_address: u32 = 0,
-/// Current size of the `.text` section on disk, must be a multiple of `file_alignment`
-text_section_size: u32 = 0,
-
-offset_table_size_dirty: bool = false,
-text_section_size_dirty: bool = false,
-/// This flag is set when the virtual size of the whole image file when loaded in memory has changed
-/// and needs to be updated in the optional header.
-size_of_image_dirty: bool = false,
-
-pub const TextBlock = struct {
- /// Offset of the code relative to the start of the text section
- text_offset: u32,
- /// Used size of the text block
- size: u32,
- /// This field is undefined for symbols with size = 0.
- offset_table_index: u32,
- /// Points to the previous and next neighbors, based on the `text_offset`.
- /// This can be used to find, for example, the capacity of this `TextBlock`.
- prev: ?*TextBlock,
- next: ?*TextBlock,
-
- pub const empty = TextBlock{
- .text_offset = 0,
- .size = 0,
- .offset_table_index = undefined,
- .prev = null,
- .next = null,
- };
-
- /// Returns how much room there is to grow in virtual address space.
- fn capacity(self: TextBlock) u64 {
- if (self.next) |next| {
- return next.text_offset - self.text_offset;
- }
- // This is the last block, the capacity is only limited by the address space.
- return std.math.maxInt(u32) - self.text_offset;
- }
-
- fn freeListEligible(self: TextBlock) bool {
- // No need to keep a free list node for the last block.
- const next = self.next orelse return false;
- const cap = next.text_offset - self.text_offset;
- const ideal_cap = self.size * allocation_padding;
- if (cap <= ideal_cap) return false;
- const surplus = cap - ideal_cap;
- return surplus >= minimum_text_block_size;
- }
-
- /// Absolute virtual address of the text block when the file is loaded in memory.
- fn getVAddr(self: TextBlock, coff: Coff) u32 {
- return coff.text_section_virtual_address + self.text_offset;
- }
-};
-
-pub const SrcFn = void;
-
-pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*link.File {
- assert(options.object_format == .coff);
-
- const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) });
- errdefer file.close();
-
- var coff_file = try allocator.create(Coff);
- errdefer allocator.destroy(coff_file);
-
- coff_file.* = openFile(allocator, file, options) catch |err| switch (err) {
- error.IncrFailed => try createFile(allocator, file, options),
- else => |e| return e,
- };
-
- return &coff_file.base;
-}
-
-/// Returns error.IncrFailed if incremental update could not be performed.
-fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff {
- switch (options.output_mode) {
- .Exe => {},
- .Obj => return error.IncrFailed,
- .Lib => return error.IncrFailed,
- }
- var self: Coff = .{
- .base = .{
- .file = file,
- .tag = .coff,
- .options = options,
- .allocator = allocator,
- },
- .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
- 32 => .p32,
- 64 => .p64,
- else => return error.UnsupportedELFArchitecture,
- },
- };
- errdefer self.deinit();
-
- // TODO implement reading the PE/COFF file
- return error.IncrFailed;
-}
-
-/// Truncates the existing file contents and overwrites the contents.
-/// Returns an error if `file` is not already open with +read +write +seek abilities.
-fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff {
- // TODO Write object specific relocations, COFF symbol table, then enable object file output.
- switch (options.output_mode) {
- .Exe => {},
- .Obj => return error.TODOImplementWritingObjFiles,
- .Lib => return error.TODOImplementWritingLibFiles,
- }
- var self: Coff = .{
- .base = .{
- .tag = .coff,
- .options = options,
- .allocator = allocator,
- .file = file,
- },
- .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
- 32 => .p32,
- 64 => .p64,
- else => return error.UnsupportedCOFFArchitecture,
- },
- };
- errdefer self.deinit();
-
- var coff_file_header_offset: u32 = 0;
- if (options.output_mode == .Exe) {
- // Write the MS-DOS stub and the PE signature
- try self.base.file.?.pwriteAll(msdos_stub ++ "PE\x00\x00", 0);
- coff_file_header_offset = msdos_stub.len + 4;
- }
-
- // COFF file header
- const data_directory_count = 0;
- var hdr_data: [112 + data_directory_count * 8 + section_table_size]u8 = undefined;
- var index: usize = 0;
-
- const machine = self.base.options.target.cpu.arch.toCoffMachine();
- if (machine == .Unknown) {
- return error.UnsupportedCOFFArchitecture;
- }
- std.mem.writeIntLittle(u16, hdr_data[0..2], @enumToInt(machine));
- index += 2;
-
- // Number of sections (we only use .got, .text)
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 2);
- index += 2;
- // TimeDateStamp (u32), PointerToSymbolTable (u32), NumberOfSymbols (u32)
- std.mem.set(u8, hdr_data[index..][0..12], 0);
- index += 12;
-
- const optional_header_size = switch (options.output_mode) {
- .Exe => data_directory_count * 8 + switch (self.ptr_width) {
- .p32 => @as(u16, 96),
- .p64 => 112,
- },
- else => 0,
- };
-
- const section_table_offset = coff_file_header_offset + 20 + optional_header_size;
- const default_offset_table_size = file_alignment;
- const default_size_of_code = 0;
-
- self.section_data_offset = std.mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment);
- const section_data_relative_virtual_address = std.mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment);
- self.offset_table_virtual_address = image_base + section_data_relative_virtual_address;
- self.offset_table_size = default_offset_table_size;
- self.section_table_offset = section_table_offset;
- self.text_section_virtual_address = image_base + section_data_relative_virtual_address + section_alignment;
- self.text_section_size = default_size_of_code;
-
- // Size of file when loaded in memory
- const size_of_image = std.mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + default_size_of_code, section_alignment);
-
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size);
- index += 2;
-
- // Characteristics
- var characteristics: u16 = std.coff.IMAGE_FILE_DEBUG_STRIPPED | std.coff.IMAGE_FILE_RELOCS_STRIPPED; // TODO Remove debug info stripped flag when necessary
- if (options.output_mode == .Exe) {
- characteristics |= std.coff.IMAGE_FILE_EXECUTABLE_IMAGE;
- }
- switch (self.ptr_width) {
- .p32 => characteristics |= std.coff.IMAGE_FILE_32BIT_MACHINE,
- .p64 => characteristics |= std.coff.IMAGE_FILE_LARGE_ADDRESS_AWARE,
- }
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], characteristics);
- index += 2;
-
- assert(index == 20);
- try self.base.file.?.pwriteAll(hdr_data[0..index], coff_file_header_offset);
-
- if (options.output_mode == .Exe) {
- self.optional_header_offset = coff_file_header_offset + 20;
- // Optional header
- index = 0;
- std.mem.writeIntLittle(u16, hdr_data[0..2], switch (self.ptr_width) {
- .p32 => @as(u16, 0x10b),
- .p64 => 0x20b,
- });
- index += 2;
-
- // Linker version (u8 + u8)
- std.mem.set(u8, hdr_data[index..][0..2], 0);
- index += 2;
-
- // SizeOfCode (UNUSED, u32), SizeOfInitializedData (u32), SizeOfUninitializedData (u32), AddressOfEntryPoint (u32), BaseOfCode (UNUSED, u32)
- std.mem.set(u8, hdr_data[index..][0..20], 0);
- index += 20;
-
- if (self.ptr_width == .p32) {
- // Base of data relative to the image base (UNUSED)
- std.mem.set(u8, hdr_data[index..][0..4], 0);
- index += 4;
-
- // Image base address
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], image_base);
- index += 4;
- } else {
- // Image base address
- std.mem.writeIntLittle(u64, hdr_data[index..][0..8], image_base);
- index += 8;
- }
-
- // Section alignment
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], section_alignment);
- index += 4;
- // File alignment
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], file_alignment);
- index += 4;
- // Required OS version, 6.0 is vista
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
- index += 2;
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
- index += 2;
- // Image version
- std.mem.set(u8, hdr_data[index..][0..4], 0);
- index += 4;
- // Required subsystem version, same as OS version
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
- index += 2;
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
- index += 2;
- // Reserved zeroes (u32)
- std.mem.set(u8, hdr_data[index..][0..4], 0);
- index += 4;
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], size_of_image);
- index += 4;
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
- index += 4;
- // CheckSum (u32)
- std.mem.set(u8, hdr_data[index..][0..4], 0);
- index += 4;
- // Subsystem, TODO: Let users specify the subsystem, always CUI for now
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 3);
- index += 2;
- // DLL characteristics
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0x0);
- index += 2;
-
- switch (self.ptr_width) {
- .p32 => {
- // Size of stack reserve + commit
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000_000);
- index += 4;
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
- index += 4;
- // Size of heap reserve + commit
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x100_000);
- index += 4;
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
- index += 4;
- },
- .p64 => {
- // Size of stack reserve + commit
- std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000_000);
- index += 8;
- std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
- index += 8;
- // Size of heap reserve + commit
- std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x100_000);
- index += 8;
- std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
- index += 8;
- },
- }
-
- // Reserved zeroes
- std.mem.set(u8, hdr_data[index..][0..4], 0);
- index += 4;
-
- // Number of data directories
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], data_directory_count);
- index += 4;
- // Initialize data directories to zero
- std.mem.set(u8, hdr_data[index..][0 .. data_directory_count * 8], 0);
- index += data_directory_count * 8;
-
- assert(index == optional_header_size);
- }
-
- // Write section table.
- // First, the .got section
- hdr_data[index..][0..8].* = ".got\x00\x00\x00\x00".*;
- index += 8;
- if (options.output_mode == .Exe) {
- // Virtual size (u32)
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
- index += 4;
- // Virtual address (u32)
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - image_base);
- index += 4;
- } else {
- std.mem.set(u8, hdr_data[index..][0..8], 0);
- index += 8;
- }
- // Size of raw data (u32)
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
- index += 4;
- // File pointer to the start of the section
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
- index += 4;
- // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16)
- std.mem.set(u8, hdr_data[index..][0..12], 0);
- index += 12;
- // Section flags
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], std.coff.IMAGE_SCN_CNT_INITIALIZED_DATA | std.coff.IMAGE_SCN_MEM_READ);
- index += 4;
- // Then, the .text section
- hdr_data[index..][0..8].* = ".text\x00\x00\x00".*;
- index += 8;
- if (options.output_mode == .Exe) {
- // Virtual size (u32)
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
- index += 4;
- // Virtual address (u32)
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - image_base);
- index += 4;
- } else {
- std.mem.set(u8, hdr_data[index..][0..8], 0);
- index += 8;
- }
- // Size of raw data (u32)
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
- index += 4;
- // File pointer to the start of the section
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset + default_offset_table_size);
- index += 4;
- // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16)
- std.mem.set(u8, hdr_data[index..][0..12], 0);
- index += 12;
- // Section flags
- std.mem.writeIntLittle(
- u32,
- hdr_data[index..][0..4],
- std.coff.IMAGE_SCN_CNT_CODE | std.coff.IMAGE_SCN_MEM_EXECUTE | std.coff.IMAGE_SCN_MEM_READ | std.coff.IMAGE_SCN_MEM_WRITE,
- );
- index += 4;
-
- assert(index == optional_header_size + section_table_size);
- try self.base.file.?.pwriteAll(hdr_data[0..index], self.optional_header_offset);
- try self.base.file.?.setEndPos(self.section_data_offset + default_offset_table_size + default_size_of_code);
-
- return self;
-}
-
-pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void {
- try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
-
- if (self.offset_table_free_list.popOrNull()) |i| {
- decl.link.coff.offset_table_index = i;
- } else {
- decl.link.coff.offset_table_index = @intCast(u32, self.offset_table.items.len);
- _ = self.offset_table.addOneAssumeCapacity();
-
- const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8;
- if (self.offset_table.items.len > self.offset_table_size / entry_size) {
- self.offset_table_size_dirty = true;
- }
- }
-
- self.offset_table.items[decl.link.coff.offset_table_index] = 0;
-}
-
-fn allocateTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
- const new_block_min_capacity = new_block_size * allocation_padding;
-
- // We use these to indicate our intention to update metadata, placing the new block,
- // and possibly removing a free list node.
- // It would be simpler to do it inside the for loop below, but that would cause a
- // problem if an error was returned later in the function. So this action
- // is actually carried out at the end of the function, when errors are no longer possible.
- var block_placement: ?*TextBlock = null;
- var free_list_removal: ?usize = null;
-
- const vaddr = blk: {
- var i: usize = 0;
- while (i < self.text_block_free_list.items.len) {
- const free_block = self.text_block_free_list.items[i];
-
- const next_block_text_offset = free_block.text_offset + free_block.capacity();
- const new_block_text_offset = std.mem.alignForwardGeneric(u64, free_block.getVAddr(self.*) + free_block.size, alignment) - self.text_section_virtual_address;
- if (new_block_text_offset < next_block_text_offset and next_block_text_offset - new_block_text_offset >= new_block_min_capacity) {
- block_placement = free_block;
-
- const remaining_capacity = next_block_text_offset - new_block_text_offset - new_block_min_capacity;
- if (remaining_capacity < minimum_text_block_size) {
- free_list_removal = i;
- }
-
- break :blk new_block_text_offset + self.text_section_virtual_address;
- } else {
- if (!free_block.freeListEligible()) {
- _ = self.text_block_free_list.swapRemove(i);
- } else {
- i += 1;
- }
- continue;
- }
- } else if (self.last_text_block) |last| {
- const new_block_vaddr = std.mem.alignForwardGeneric(u64, last.getVAddr(self.*) + last.size, alignment);
- block_placement = last;
- break :blk new_block_vaddr;
- } else {
- break :blk self.text_section_virtual_address;
- }
- };
-
- const expand_text_section = block_placement == null or block_placement.?.next == null;
- if (expand_text_section) {
- const needed_size = @intCast(u32, std.mem.alignForwardGeneric(u64, vaddr + new_block_size - self.text_section_virtual_address, file_alignment));
- if (needed_size > self.text_section_size) {
- const current_text_section_virtual_size = std.mem.alignForwardGeneric(u32, self.text_section_size, section_alignment);
- const new_text_section_virtual_size = std.mem.alignForwardGeneric(u32, needed_size, section_alignment);
- if (current_text_section_virtual_size != new_text_section_virtual_size) {
- self.size_of_image_dirty = true;
- // Write new virtual size
- var buf: [4]u8 = undefined;
- std.mem.writeIntLittle(u32, &buf, new_text_section_virtual_size);
- try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 8);
- }
-
- self.text_section_size = needed_size;
- self.text_section_size_dirty = true;
- }
- self.last_text_block = text_block;
- }
- text_block.text_offset = @intCast(u32, vaddr - self.text_section_virtual_address);
- text_block.size = @intCast(u32, new_block_size);
-
- // This function can also reallocate a text block.
- // In this case we need to "unplug" it from its previous location before
- // plugging it in to its new location.
- if (text_block.prev) |prev| {
- prev.next = text_block.next;
- }
- if (text_block.next) |next| {
- next.prev = text_block.prev;
- }
-
- if (block_placement) |big_block| {
- text_block.prev = big_block;
- text_block.next = big_block.next;
- big_block.next = text_block;
- } else {
- text_block.prev = null;
- text_block.next = null;
- }
- if (free_list_removal) |i| {
- _ = self.text_block_free_list.swapRemove(i);
- }
- return vaddr;
-}
-
-fn growTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
- const block_vaddr = text_block.getVAddr(self.*);
- const align_ok = std.mem.alignBackwardGeneric(u64, block_vaddr, alignment) == block_vaddr;
- const need_realloc = !align_ok or new_block_size > text_block.capacity();
- if (!need_realloc) return @as(u64, block_vaddr);
- return self.allocateTextBlock(text_block, new_block_size, alignment);
-}
-
-fn shrinkTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64) void {
- text_block.size = @intCast(u32, new_block_size);
- if (text_block.capacity() - text_block.size >= minimum_text_block_size) {
- self.text_block_free_list.append(self.base.allocator, text_block) catch {};
- }
-}
-
-fn freeTextBlock(self: *Coff, text_block: *TextBlock) void {
- var already_have_free_list_node = false;
- {
- var i: usize = 0;
- // TODO turn text_block_free_list into a hash map
- while (i < self.text_block_free_list.items.len) {
- if (self.text_block_free_list.items[i] == text_block) {
- _ = self.text_block_free_list.swapRemove(i);
- continue;
- }
- if (self.text_block_free_list.items[i] == text_block.prev) {
- already_have_free_list_node = true;
- }
- i += 1;
- }
- }
- if (self.last_text_block == text_block) {
- self.last_text_block = text_block.prev;
- }
- if (text_block.prev) |prev| {
- prev.next = text_block.next;
-
- if (!already_have_free_list_node and prev.freeListEligible()) {
- // The free list is heuristics, it doesn't have to be perfect, so we can
- // ignore the OOM here.
- self.text_block_free_list.append(self.base.allocator, prev) catch {};
- }
- }
-
- if (text_block.next) |next| {
- next.prev = text_block.prev;
- }
-}
-
-fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
- const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8;
- const endian = self.base.options.target.cpu.arch.endian();
-
- const offset_table_start = self.section_data_offset;
- if (self.offset_table_size_dirty) {
- const current_raw_size = self.offset_table_size;
- const new_raw_size = self.offset_table_size * 2;
- log.debug("growing offset table from raw size {} to {}\n", .{ current_raw_size, new_raw_size });
-
- // Move the text section to a new place in the executable
- const current_text_section_start = self.section_data_offset + current_raw_size;
- const new_text_section_start = self.section_data_offset + new_raw_size;
-
- const amt = try self.base.file.?.copyRangeAll(current_text_section_start, self.base.file.?, new_text_section_start, self.text_section_size);
- if (amt != self.text_section_size) return error.InputOutput;
-
- // Write the new raw size in the .got header
- var buf: [8]u8 = undefined;
- std.mem.writeIntLittle(u32, buf[0..4], new_raw_size);
- try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 16);
- // Write the new .text section file offset in the .text section header
- std.mem.writeIntLittle(u32, buf[0..4], new_text_section_start);
- try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 20);
-
- const current_virtual_size = std.mem.alignForwardGeneric(u32, self.offset_table_size, section_alignment);
- const new_virtual_size = std.mem.alignForwardGeneric(u32, new_raw_size, section_alignment);
- // If we had to move in the virtual address space, we need to fix the VAs in the offset table, as well as the virtual address of the `.text` section
- // and the virutal size of the `.got` section
-
- if (new_virtual_size != current_virtual_size) {
- log.debug("growing offset table from virtual size {} to {}\n", .{ current_virtual_size, new_virtual_size });
- self.size_of_image_dirty = true;
- const va_offset = new_virtual_size - current_virtual_size;
-
- // Write .got virtual size
- std.mem.writeIntLittle(u32, buf[0..4], new_virtual_size);
- try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 8);
-
- // Write .text new virtual address
- self.text_section_virtual_address = self.text_section_virtual_address + va_offset;
- std.mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - image_base);
- try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12);
-
- // Fix the VAs in the offset table
- for (self.offset_table.items) |*va, idx| {
- if (va.* != 0) {
- va.* += va_offset;
-
- switch (entry_size) {
- 4 => {
- std.mem.writeInt(u32, buf[0..4], @intCast(u32, va.*), endian);
- try self.base.file.?.pwriteAll(buf[0..4], offset_table_start + idx * entry_size);
- },
- 8 => {
- std.mem.writeInt(u64, &buf, va.*, endian);
- try self.base.file.?.pwriteAll(&buf, offset_table_start + idx * entry_size);
- },
- else => unreachable,
- }
- }
- }
- }
- self.offset_table_size = new_raw_size;
- self.offset_table_size_dirty = false;
- }
- // Write the new entry
- switch (entry_size) {
- 4 => {
- var buf: [4]u8 = undefined;
- std.mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
- try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size);
- },
- 8 => {
- var buf: [8]u8 = undefined;
- std.mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
- try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size);
- },
- else => unreachable,
- }
-}
-
-pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
- // TODO COFF/PE debug information
- // TODO Implement exports
- const tracy = trace(@src());
- defer tracy.end();
-
- var code_buffer = std.ArrayList(u8).init(self.base.allocator);
- defer code_buffer.deinit();
-
- const typed_value = decl.typed_value.most_recent.typed_value;
- const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .none);
- const code = switch (res) {
- .externally_managed => |x| x,
- .appended => code_buffer.items,
- .fail => |em| {
- decl.analysis = .codegen_failure;
- try module.failed_decls.put(module.gpa, decl, em);
- return;
- },
- };
-
- const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
- const curr_size = decl.link.coff.size;
- if (curr_size != 0) {
- const capacity = decl.link.coff.capacity();
- const need_realloc = code.len > capacity or
- !std.mem.isAlignedGeneric(u32, decl.link.coff.text_offset, required_alignment);
- if (need_realloc) {
- const curr_vaddr = self.getDeclVAddr(decl);
- const vaddr = try self.growTextBlock(&decl.link.coff, code.len, required_alignment);
- log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, curr_vaddr, vaddr });
- if (vaddr != curr_vaddr) {
- log.debug(" (writing new offset table entry)\n", .{});
- self.offset_table.items[decl.link.coff.offset_table_index] = vaddr;
- try self.writeOffsetTableEntry(decl.link.coff.offset_table_index);
- }
- } else if (code.len < curr_size) {
- self.shrinkTextBlock(&decl.link.coff, code.len);
- }
- } else {
- const vaddr = try self.allocateTextBlock(&decl.link.coff, code.len, required_alignment);
- log.debug("allocated text block for {} at 0x{x} (size: {Bi})\n", .{ std.mem.spanZ(decl.name), vaddr, code.len });
- errdefer self.freeTextBlock(&decl.link.coff);
- self.offset_table.items[decl.link.coff.offset_table_index] = vaddr;
- try self.writeOffsetTableEntry(decl.link.coff.offset_table_index);
- }
-
- // Write the code into the file
- try self.base.file.?.pwriteAll(code, self.section_data_offset + self.offset_table_size + decl.link.coff.text_offset);
-
- // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
- const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
- return self.updateDeclExports(module, decl, decl_exports);
-}
-
-pub fn freeDecl(self: *Coff, decl: *Module.Decl) void {
- // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
- self.freeTextBlock(&decl.link.coff);
- self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {};
-}
-
-pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, exports: []const *Module.Export) !void {
- for (exports) |exp| {
- if (exp.options.section) |section_name| {
- if (!std.mem.eql(u8, section_name, ".text")) {
- try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
- module.failed_exports.putAssumeCapacityNoClobber(
- exp,
- try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
- );
- continue;
- }
- }
- if (std.mem.eql(u8, exp.options.name, "_start")) {
- self.entry_addr = decl.link.coff.getVAddr(self.*) - image_base;
- } else {
- try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
- module.failed_exports.putAssumeCapacityNoClobber(
- exp,
- try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: Exports other than '_start'", .{}),
- );
- continue;
- }
- }
-}
-
-pub fn flush(self: *Coff, module: *Module) !void {
- if (self.text_section_size_dirty) {
- // Write the new raw size in the .text header
- var buf: [4]u8 = undefined;
- std.mem.writeIntLittle(u32, &buf, self.text_section_size);
- try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 16);
- try self.base.file.?.setEndPos(self.section_data_offset + self.offset_table_size + self.text_section_size);
- self.text_section_size_dirty = false;
- }
-
- if (self.base.options.output_mode == .Exe and self.size_of_image_dirty) {
- const new_size_of_image = std.mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + self.text_section_size, section_alignment);
- var buf: [4]u8 = undefined;
- std.mem.writeIntLittle(u32, &buf, new_size_of_image);
- try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56);
- self.size_of_image_dirty = false;
- }
-
- if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
- log.debug("flushing. no_entry_point_found = true\n", .{});
- self.error_flags.no_entry_point_found = true;
- } else {
- log.debug("flushing. no_entry_point_found = false\n", .{});
- self.error_flags.no_entry_point_found = false;
-
- if (self.base.options.output_mode == .Exe) {
- // Write AddressOfEntryPoint
- var buf: [4]u8 = undefined;
- std.mem.writeIntLittle(u32, &buf, self.entry_addr.?);
- try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 16);
- }
- }
-}
-
-pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 {
- return self.text_section_virtual_address + decl.link.coff.text_offset;
-}
-
-pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void {
- // TODO Implement this
-}
-
-pub fn deinit(self: *Coff) void {
- self.text_block_free_list.deinit(self.base.allocator);
- self.offset_table.deinit(self.base.allocator);
- self.offset_table_free_list.deinit(self.base.allocator);
-}
diff --git a/src-self-hosted/link/Wasm.zig b/src-self-hosted/link/Wasm.zig
deleted file mode 100644
index d8f172f584..0000000000
--- a/src-self-hosted/link/Wasm.zig
+++ /dev/null
@@ -1,251 +0,0 @@
-const Wasm = @This();
-
-const std = @import("std");
-const Allocator = std.mem.Allocator;
-const assert = std.debug.assert;
-const fs = std.fs;
-const leb = std.debug.leb;
-
-const Module = @import("../Module.zig");
-const codegen = @import("../codegen/wasm.zig");
-const link = @import("../link.zig");
-
-/// Various magic numbers defined by the wasm spec
-const spec = struct {
- const magic = [_]u8{ 0x00, 0x61, 0x73, 0x6D }; // \0asm
- const version = [_]u8{ 0x01, 0x00, 0x00, 0x00 }; // version 1
-
- const custom_id = 0;
- const types_id = 1;
- const imports_id = 2;
- const funcs_id = 3;
- const tables_id = 4;
- const memories_id = 5;
- const globals_id = 6;
- const exports_id = 7;
- const start_id = 8;
- const elements_id = 9;
- const code_id = 10;
- const data_id = 11;
-};
-
-pub const base_tag = link.File.Tag.wasm;
-
-pub const FnData = struct {
- /// Generated code for the type of the function
- functype: std.ArrayListUnmanaged(u8) = .{},
- /// Generated code for the body of the function
- code: std.ArrayListUnmanaged(u8) = .{},
- /// Locations in the generated code where function indexes must be filled in.
- /// This must be kept ordered by offset.
- idx_refs: std.ArrayListUnmanaged(struct { offset: u32, decl: *Module.Decl }) = .{},
-};
-
-base: link.File,
-
-/// List of all function Decls to be written to the output file. The index of
-/// each Decl in this list at the time of writing the binary is used as the
-/// function index.
-/// TODO: can/should we access some data structure in Module directly?
-funcs: std.ArrayListUnmanaged(*Module.Decl) = .{},
-
-pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*link.File {
- assert(options.object_format == .wasm);
-
- // TODO: read the file and keep vaild parts instead of truncating
- const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true });
- errdefer file.close();
-
- const wasm = try allocator.create(Wasm);
- errdefer allocator.destroy(wasm);
-
- try file.writeAll(&(spec.magic ++ spec.version));
-
- wasm.* = .{
- .base = .{
- .tag = .wasm,
- .options = options,
- .file = file,
- .allocator = allocator,
- },
- };
-
- return &wasm.base;
-}
-
-pub fn deinit(self: *Wasm) void {
- for (self.funcs.items) |decl| {
- decl.fn_link.wasm.?.functype.deinit(self.base.allocator);
- decl.fn_link.wasm.?.code.deinit(self.base.allocator);
- decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator);
- }
- self.funcs.deinit(self.base.allocator);
-}
-
-// Generate code for the Decl, storing it in memory to be later written to
-// the file on flush().
-pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
- if (decl.typed_value.most_recent.typed_value.ty.zigTypeTag() != .Fn)
- return error.TODOImplementNonFnDeclsForWasm;
-
- if (decl.fn_link.wasm) |*fn_data| {
- fn_data.functype.items.len = 0;
- fn_data.code.items.len = 0;
- fn_data.idx_refs.items.len = 0;
- } else {
- decl.fn_link.wasm = .{};
- try self.funcs.append(self.base.allocator, decl);
- }
- const fn_data = &decl.fn_link.wasm.?;
-
- var managed_functype = fn_data.functype.toManaged(self.base.allocator);
- var managed_code = fn_data.code.toManaged(self.base.allocator);
- try codegen.genFunctype(&managed_functype, decl);
- try codegen.genCode(&managed_code, decl);
- fn_data.functype = managed_functype.toUnmanaged();
- fn_data.code = managed_code.toUnmanaged();
-}
-
-pub fn updateDeclExports(
- self: *Wasm,
- module: *Module,
- decl: *const Module.Decl,
- exports: []const *Module.Export,
-) !void {}
-
-pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
- // TODO: remove this assert when non-function Decls are implemented
- assert(decl.typed_value.most_recent.typed_value.ty.zigTypeTag() == .Fn);
- _ = self.funcs.swapRemove(self.getFuncidx(decl).?);
- decl.fn_link.wasm.?.functype.deinit(self.base.allocator);
- decl.fn_link.wasm.?.code.deinit(self.base.allocator);
- decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator);
- decl.fn_link.wasm = null;
-}
-
-pub fn flush(self: *Wasm, module: *Module) !void {
- const file = self.base.file.?;
- const header_size = 5 + 1;
-
- // No need to rewrite the magic/version header
- try file.setEndPos(@sizeOf(@TypeOf(spec.magic ++ spec.version)));
- try file.seekTo(@sizeOf(@TypeOf(spec.magic ++ spec.version)));
-
- // Type section
- {
- const header_offset = try reserveVecSectionHeader(file);
- for (self.funcs.items) |decl| {
- try file.writeAll(decl.fn_link.wasm.?.functype.items);
- }
- try writeVecSectionHeader(
- file,
- header_offset,
- spec.types_id,
- @intCast(u32, (try file.getPos()) - header_offset - header_size),
- @intCast(u32, self.funcs.items.len),
- );
- }
-
- // Function section
- {
- const header_offset = try reserveVecSectionHeader(file);
- const writer = file.writer();
- for (self.funcs.items) |_, typeidx| try leb.writeULEB128(writer, @intCast(u32, typeidx));
- try writeVecSectionHeader(
- file,
- header_offset,
- spec.funcs_id,
- @intCast(u32, (try file.getPos()) - header_offset - header_size),
- @intCast(u32, self.funcs.items.len),
- );
- }
-
- // Export section
- {
- const header_offset = try reserveVecSectionHeader(file);
- const writer = file.writer();
- var count: u32 = 0;
- for (module.decl_exports.entries.items) |entry| {
- for (entry.value) |exprt| {
- // Export name length + name
- try leb.writeULEB128(writer, @intCast(u32, exprt.options.name.len));
- try writer.writeAll(exprt.options.name);
-
- switch (exprt.exported_decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
- .Fn => {
- // Type of the export
- try writer.writeByte(0x00);
- // Exported function index
- try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).?);
- },
- else => return error.TODOImplementNonFnDeclsForWasm,
- }
-
- count += 1;
- }
- }
- try writeVecSectionHeader(
- file,
- header_offset,
- spec.exports_id,
- @intCast(u32, (try file.getPos()) - header_offset - header_size),
- count,
- );
- }
-
- // Code section
- {
- const header_offset = try reserveVecSectionHeader(file);
- const writer = file.writer();
- for (self.funcs.items) |decl| {
- const fn_data = &decl.fn_link.wasm.?;
-
- // Write the already generated code to the file, inserting
- // function indexes where required.
- var current: u32 = 0;
- for (fn_data.idx_refs.items) |idx_ref| {
- try writer.writeAll(fn_data.code.items[current..idx_ref.offset]);
- current = idx_ref.offset;
- // Use a fixed width here to make calculating the code size
- // in codegen.wasm.genCode() simpler.
- var buf: [5]u8 = undefined;
- leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).?);
- try writer.writeAll(&buf);
- }
-
- try writer.writeAll(fn_data.code.items[current..]);
- }
- try writeVecSectionHeader(
- file,
- header_offset,
- spec.code_id,
- @intCast(u32, (try file.getPos()) - header_offset - header_size),
- @intCast(u32, self.funcs.items.len),
- );
- }
-}
-
-/// Get the current index of a given Decl in the function list
-/// TODO: we could maintain a hash map to potentially make this
-fn getFuncidx(self: Wasm, decl: *Module.Decl) ?u32 {
- return for (self.funcs.items) |func, idx| {
- if (func == decl) break @intCast(u32, idx);
- } else null;
-}
-
-fn reserveVecSectionHeader(file: fs.File) !u64 {
- // section id + fixed leb contents size + fixed leb vector length
- const header_size = 1 + 5 + 5;
- // TODO: this should be a single lseek(2) call, but fs.File does not
- // currently provide a way to do this.
- try file.seekBy(header_size);
- return (try file.getPos()) - header_size;
-}
-
-fn writeVecSectionHeader(file: fs.File, offset: u64, section: u8, size: u32, items: u32) !void {
- var buf: [1 + 5 + 5]u8 = undefined;
- buf[0] = section;
- leb.writeUnsignedFixed(5, buf[1..6], size);
- leb.writeUnsignedFixed(5, buf[6..], items);
- try file.pwriteAll(&buf, offset);
-}
diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig
deleted file mode 100644
index 4159968252..0000000000
--- a/src-self-hosted/llvm.zig
+++ /dev/null
@@ -1,293 +0,0 @@
-const c = @import("c.zig");
-const assert = @import("std").debug.assert;
-
-// we wrap the c module for 3 reasons:
-// 1. to avoid accidentally calling the non-thread-safe functions
-// 2. patch up some of the types to remove nullability
-// 3. some functions have been augmented by zig_llvm.cpp to be more powerful,
-// such as ZigLLVMTargetMachineEmitToFile
-
-pub const AttributeIndex = c_uint;
-pub const Bool = c_int;
-
-pub const Builder = c.LLVMBuilderRef.Child.Child;
-pub const Context = c.LLVMContextRef.Child.Child;
-pub const Module = c.LLVMModuleRef.Child.Child;
-pub const Value = c.LLVMValueRef.Child.Child;
-pub const Type = c.LLVMTypeRef.Child.Child;
-pub const BasicBlock = c.LLVMBasicBlockRef.Child.Child;
-pub const Attribute = c.LLVMAttributeRef.Child.Child;
-pub const Target = c.LLVMTargetRef.Child.Child;
-pub const TargetMachine = c.LLVMTargetMachineRef.Child.Child;
-pub const TargetData = c.LLVMTargetDataRef.Child.Child;
-pub const DIBuilder = c.ZigLLVMDIBuilder;
-pub const DIFile = c.ZigLLVMDIFile;
-pub const DICompileUnit = c.ZigLLVMDICompileUnit;
-
-pub const ABIAlignmentOfType = c.LLVMABIAlignmentOfType;
-pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex;
-pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag;
-pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag;
-pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation;
-pub const ConstAllOnes = c.LLVMConstAllOnes;
-pub const ConstArray = c.LLVMConstArray;
-pub const ConstBitCast = c.LLVMConstBitCast;
-pub const ConstIntOfArbitraryPrecision = c.LLVMConstIntOfArbitraryPrecision;
-pub const ConstNeg = c.LLVMConstNeg;
-pub const ConstStructInContext = c.LLVMConstStructInContext;
-pub const DIBuilderFinalize = c.ZigLLVMDIBuilderFinalize;
-pub const DisposeBuilder = c.LLVMDisposeBuilder;
-pub const DisposeDIBuilder = c.ZigLLVMDisposeDIBuilder;
-pub const DisposeMessage = c.LLVMDisposeMessage;
-pub const DisposeModule = c.LLVMDisposeModule;
-pub const DisposeTargetData = c.LLVMDisposeTargetData;
-pub const DisposeTargetMachine = c.LLVMDisposeTargetMachine;
-pub const DoubleTypeInContext = c.LLVMDoubleTypeInContext;
-pub const DumpModule = c.LLVMDumpModule;
-pub const FP128TypeInContext = c.LLVMFP128TypeInContext;
-pub const FloatTypeInContext = c.LLVMFloatTypeInContext;
-pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName;
-pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext;
-pub const GetUndef = c.LLVMGetUndef;
-pub const HalfTypeInContext = c.LLVMHalfTypeInContext;
-pub const InitializeAllAsmParsers = c.LLVMInitializeAllAsmParsers;
-pub const InitializeAllAsmPrinters = c.LLVMInitializeAllAsmPrinters;
-pub const InitializeAllTargetInfos = c.LLVMInitializeAllTargetInfos;
-pub const InitializeAllTargetMCs = c.LLVMInitializeAllTargetMCs;
-pub const InitializeAllTargets = c.LLVMInitializeAllTargets;
-pub const InsertBasicBlockInContext = c.LLVMInsertBasicBlockInContext;
-pub const Int128TypeInContext = c.LLVMInt128TypeInContext;
-pub const Int16TypeInContext = c.LLVMInt16TypeInContext;
-pub const Int1TypeInContext = c.LLVMInt1TypeInContext;
-pub const Int32TypeInContext = c.LLVMInt32TypeInContext;
-pub const Int64TypeInContext = c.LLVMInt64TypeInContext;
-pub const Int8TypeInContext = c.LLVMInt8TypeInContext;
-pub const IntPtrTypeForASInContext = c.LLVMIntPtrTypeForASInContext;
-pub const IntPtrTypeInContext = c.LLVMIntPtrTypeInContext;
-pub const LabelTypeInContext = c.LLVMLabelTypeInContext;
-pub const MDNodeInContext = c.LLVMMDNodeInContext;
-pub const MDStringInContext = c.LLVMMDStringInContext;
-pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext;
-pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext;
-pub const SetAlignment = c.LLVMSetAlignment;
-pub const SetDataLayout = c.LLVMSetDataLayout;
-pub const SetGlobalConstant = c.LLVMSetGlobalConstant;
-pub const SetInitializer = c.LLVMSetInitializer;
-pub const SetLinkage = c.LLVMSetLinkage;
-pub const SetTarget = c.LLVMSetTarget;
-pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr;
-pub const SetVolatile = c.LLVMSetVolatile;
-pub const StructTypeInContext = c.LLVMStructTypeInContext;
-pub const TokenTypeInContext = c.LLVMTokenTypeInContext;
-pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext;
-pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext;
-
-pub const AddGlobal = LLVMAddGlobal;
-extern fn LLVMAddGlobal(M: *Module, Ty: *Type, Name: [*:0]const u8) ?*Value;
-
-pub const ConstStringInContext = LLVMConstStringInContext;
-extern fn LLVMConstStringInContext(C: *Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) ?*Value;
-
-pub const ConstInt = LLVMConstInt;
-extern fn LLVMConstInt(IntTy: *Type, N: c_ulonglong, SignExtend: Bool) ?*Value;
-
-pub const BuildLoad = LLVMBuildLoad;
-extern fn LLVMBuildLoad(arg0: *Builder, PointerVal: *Value, Name: [*:0]const u8) ?*Value;
-
-pub const ConstNull = LLVMConstNull;
-extern fn LLVMConstNull(Ty: *Type) ?*Value;
-
-pub const CreateStringAttribute = LLVMCreateStringAttribute;
-extern fn LLVMCreateStringAttribute(
- C: *Context,
- K: [*]const u8,
- KLength: c_uint,
- V: [*]const u8,
- VLength: c_uint,
-) ?*Attribute;
-
-pub const CreateEnumAttribute = LLVMCreateEnumAttribute;
-extern fn LLVMCreateEnumAttribute(C: *Context, KindID: c_uint, Val: u64) ?*Attribute;
-
-pub const AddFunction = LLVMAddFunction;
-extern fn LLVMAddFunction(M: *Module, Name: [*:0]const u8, FunctionTy: *Type) ?*Value;
-
-pub const CreateCompileUnit = ZigLLVMCreateCompileUnit;
-extern fn ZigLLVMCreateCompileUnit(
- dibuilder: *DIBuilder,
- lang: c_uint,
- difile: *DIFile,
- producer: [*:0]const u8,
- is_optimized: bool,
- flags: [*:0]const u8,
- runtime_version: c_uint,
- split_name: [*:0]const u8,
- dwo_id: u64,
- emit_debug_info: bool,
-) ?*DICompileUnit;
-
-pub const CreateFile = ZigLLVMCreateFile;
-extern fn ZigLLVMCreateFile(dibuilder: *DIBuilder, filename: [*:0]const u8, directory: [*:0]const u8) ?*DIFile;
-
-pub const ArrayType = LLVMArrayType;
-extern fn LLVMArrayType(ElementType: *Type, ElementCount: c_uint) ?*Type;
-
-pub const CreateDIBuilder = ZigLLVMCreateDIBuilder;
-extern fn ZigLLVMCreateDIBuilder(module: *Module, allow_unresolved: bool) ?*DIBuilder;
-
-pub const PointerType = LLVMPointerType;
-extern fn LLVMPointerType(ElementType: *Type, AddressSpace: c_uint) ?*Type;
-
-pub const CreateBuilderInContext = LLVMCreateBuilderInContext;
-extern fn LLVMCreateBuilderInContext(C: *Context) ?*Builder;
-
-pub const IntTypeInContext = LLVMIntTypeInContext;
-extern fn LLVMIntTypeInContext(C: *Context, NumBits: c_uint) ?*Type;
-
-pub const ModuleCreateWithNameInContext = LLVMModuleCreateWithNameInContext;
-extern fn LLVMModuleCreateWithNameInContext(ModuleID: [*:0]const u8, C: *Context) ?*Module;
-
-pub const VoidTypeInContext = LLVMVoidTypeInContext;
-extern fn LLVMVoidTypeInContext(C: *Context) ?*Type;
-
-pub const ContextCreate = LLVMContextCreate;
-extern fn LLVMContextCreate() ?*Context;
-
-pub const ContextDispose = LLVMContextDispose;
-extern fn LLVMContextDispose(C: *Context) void;
-
-pub const CopyStringRepOfTargetData = LLVMCopyStringRepOfTargetData;
-extern fn LLVMCopyStringRepOfTargetData(TD: *TargetData) ?[*:0]u8;
-
-pub const CreateTargetDataLayout = LLVMCreateTargetDataLayout;
-extern fn LLVMCreateTargetDataLayout(T: *TargetMachine) ?*TargetData;
-
-pub const CreateTargetMachine = ZigLLVMCreateTargetMachine;
-extern fn ZigLLVMCreateTargetMachine(
- T: *Target,
- Triple: [*:0]const u8,
- CPU: [*:0]const u8,
- Features: [*:0]const u8,
- Level: CodeGenOptLevel,
- Reloc: RelocMode,
- CodeModel: CodeModel,
- function_sections: bool,
-) ?*TargetMachine;
-
-pub const GetHostCPUName = LLVMGetHostCPUName;
-extern fn LLVMGetHostCPUName() ?[*:0]u8;
-
-pub const GetNativeFeatures = ZigLLVMGetNativeFeatures;
-extern fn ZigLLVMGetNativeFeatures() ?[*:0]u8;
-
-pub const GetElementType = LLVMGetElementType;
-extern fn LLVMGetElementType(Ty: *Type) *Type;
-
-pub const TypeOf = LLVMTypeOf;
-extern fn LLVMTypeOf(Val: *Value) *Type;
-
-pub const BuildStore = LLVMBuildStore;
-extern fn LLVMBuildStore(arg0: *Builder, Val: *Value, Ptr: *Value) ?*Value;
-
-pub const BuildAlloca = LLVMBuildAlloca;
-extern fn LLVMBuildAlloca(arg0: *Builder, Ty: *Type, Name: ?[*:0]const u8) ?*Value;
-
-pub const ConstInBoundsGEP = LLVMConstInBoundsGEP;
-pub extern fn LLVMConstInBoundsGEP(ConstantVal: *Value, ConstantIndices: [*]*Value, NumIndices: c_uint) ?*Value;
-
-pub const GetTargetFromTriple = LLVMGetTargetFromTriple;
-extern fn LLVMGetTargetFromTriple(Triple: [*:0]const u8, T: **Target, ErrorMessage: ?*[*:0]u8) Bool;
-
-pub const VerifyModule = LLVMVerifyModule;
-extern fn LLVMVerifyModule(M: *Module, Action: VerifierFailureAction, OutMessage: *?[*:0]u8) Bool;
-
-pub const GetInsertBlock = LLVMGetInsertBlock;
-extern fn LLVMGetInsertBlock(Builder: *Builder) *BasicBlock;
-
-pub const FunctionType = LLVMFunctionType;
-extern fn LLVMFunctionType(
- ReturnType: *Type,
- ParamTypes: [*]*Type,
- ParamCount: c_uint,
- IsVarArg: Bool,
-) ?*Type;
-
-pub const GetParam = LLVMGetParam;
-extern fn LLVMGetParam(Fn: *Value, Index: c_uint) *Value;
-
-pub const AppendBasicBlockInContext = LLVMAppendBasicBlockInContext;
-extern fn LLVMAppendBasicBlockInContext(C: *Context, Fn: *Value, Name: [*:0]const u8) ?*BasicBlock;
-
-pub const PositionBuilderAtEnd = LLVMPositionBuilderAtEnd;
-extern fn LLVMPositionBuilderAtEnd(Builder: *Builder, Block: *BasicBlock) void;
-
-pub const AbortProcessAction = VerifierFailureAction.LLVMAbortProcessAction;
-pub const PrintMessageAction = VerifierFailureAction.LLVMPrintMessageAction;
-pub const ReturnStatusAction = VerifierFailureAction.LLVMReturnStatusAction;
-pub const VerifierFailureAction = c.LLVMVerifierFailureAction;
-
-pub const CodeGenLevelNone = CodeGenOptLevel.LLVMCodeGenLevelNone;
-pub const CodeGenLevelLess = CodeGenOptLevel.LLVMCodeGenLevelLess;
-pub const CodeGenLevelDefault = CodeGenOptLevel.LLVMCodeGenLevelDefault;
-pub const CodeGenLevelAggressive = CodeGenOptLevel.LLVMCodeGenLevelAggressive;
-pub const CodeGenOptLevel = c.LLVMCodeGenOptLevel;
-
-pub const RelocDefault = RelocMode.LLVMRelocDefault;
-pub const RelocStatic = RelocMode.LLVMRelocStatic;
-pub const RelocPIC = RelocMode.LLVMRelocPIC;
-pub const RelocDynamicNoPic = RelocMode.LLVMRelocDynamicNoPic;
-pub const RelocMode = c.LLVMRelocMode;
-
-pub const CodeModelDefault = CodeModel.LLVMCodeModelDefault;
-pub const CodeModelJITDefault = CodeModel.LLVMCodeModelJITDefault;
-pub const CodeModelSmall = CodeModel.LLVMCodeModelSmall;
-pub const CodeModelKernel = CodeModel.LLVMCodeModelKernel;
-pub const CodeModelMedium = CodeModel.LLVMCodeModelMedium;
-pub const CodeModelLarge = CodeModel.LLVMCodeModelLarge;
-pub const CodeModel = c.LLVMCodeModel;
-
-pub const EmitAssembly = EmitOutputType.ZigLLVM_EmitAssembly;
-pub const EmitBinary = EmitOutputType.ZigLLVM_EmitBinary;
-pub const EmitLLVMIr = EmitOutputType.ZigLLVM_EmitLLVMIr;
-pub const EmitOutputType = c.ZigLLVM_EmitOutputType;
-
-pub const CCallConv = CallConv.LLVMCCallConv;
-pub const FastCallConv = CallConv.LLVMFastCallConv;
-pub const ColdCallConv = CallConv.LLVMColdCallConv;
-pub const WebKitJSCallConv = CallConv.LLVMWebKitJSCallConv;
-pub const AnyRegCallConv = CallConv.LLVMAnyRegCallConv;
-pub const X86StdcallCallConv = CallConv.LLVMX86StdcallCallConv;
-pub const X86FastcallCallConv = CallConv.LLVMX86FastcallCallConv;
-pub const CallConv = c.LLVMCallConv;
-
-pub const CallAttr = extern enum {
- Auto,
- NeverTail,
- NeverInline,
- AlwaysTail,
- AlwaysInline,
-};
-
-fn removeNullability(comptime T: type) type {
- comptime assert(@typeInfo(T).Pointer.size == .C);
- return *T.Child;
-}
-
-pub const BuildRet = LLVMBuildRet;
-extern fn LLVMBuildRet(arg0: *Builder, V: ?*Value) ?*Value;
-
-pub const TargetMachineEmitToFile = ZigLLVMTargetMachineEmitToFile;
-extern fn ZigLLVMTargetMachineEmitToFile(
- targ_machine_ref: *TargetMachine,
- module_ref: *Module,
- filename: [*:0]const u8,
- output_type: EmitOutputType,
- error_message: *[*:0]u8,
- is_debug: bool,
- is_small: bool,
-) bool;
-
-pub const BuildCall = ZigLLVMBuildCall;
-extern fn ZigLLVMBuildCall(B: *Builder, Fn: *Value, Args: [*]*Value, NumArgs: c_uint, CC: CallConv, fn_inline: CallAttr, Name: [*:0]const u8) ?*Value;
-
-pub const PrivateLinkage = c.LLVMLinkage.LLVMPrivateLinkage;
diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig
deleted file mode 100644
index fb20a09f1d..0000000000
--- a/src-self-hosted/main.zig
+++ /dev/null
@@ -1,927 +0,0 @@
-const std = @import("std");
-const io = std.io;
-const fs = std.fs;
-const mem = std.mem;
-const process = std.process;
-const Allocator = mem.Allocator;
-const ArrayList = std.ArrayList;
-const ast = std.zig.ast;
-const Module = @import("Module.zig");
-const link = @import("link.zig");
-const Package = @import("Package.zig");
-const zir = @import("zir.zig");
-const build_options = @import("build_options");
-
-pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
-
-pub const Color = enum {
- Auto,
- Off,
- On,
-};
-
-const usage =
- \\Usage: zig [command] [options]
- \\
- \\Commands:
- \\
- \\ build-exe [source] Create executable from source or object files
- \\ build-lib [source] Create library from source or object files
- \\ build-obj [source] Create object from source or assembly
- \\ fmt [source] Parse file and render in canonical zig format
- \\ targets List available compilation targets
- \\ env Print lib path, std path, compiler id and version
- \\ version Print version number and exit
- \\ zen Print zen of zig and exit
- \\
- \\
-;
-
-pub fn log(
- comptime level: std.log.Level,
- comptime scope: @TypeOf(.EnumLiteral),
- comptime format: []const u8,
- args: anytype,
-) void {
- // Hide anything more verbose than warn unless it was added with `-Dlog=foo`.
- if (@enumToInt(level) > @enumToInt(std.log.level) or
- @enumToInt(level) > @enumToInt(std.log.Level.warn))
- {
- const scope_name = @tagName(scope);
- const ok = comptime for (build_options.log_scopes) |log_scope| {
- if (mem.eql(u8, log_scope, scope_name))
- break true;
- } else false;
-
- if (!ok)
- return;
- }
-
- const prefix = "[" ++ @tagName(level) ++ "] " ++ "(" ++ @tagName(scope) ++ "): ";
-
- // Print the message to stderr, silently ignoring any errors
- std.debug.print(prefix ++ format ++ "\n", args);
-}
-
-var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
-
-pub fn main() !void {
- const gpa = if (std.builtin.link_libc) std.heap.c_allocator else &general_purpose_allocator.allocator;
- defer if (!std.builtin.link_libc) {
- _ = general_purpose_allocator.deinit();
- };
- var arena_instance = std.heap.ArenaAllocator.init(gpa);
- defer arena_instance.deinit();
- const arena = &arena_instance.allocator;
-
- const args = try process.argsAlloc(arena);
-
- if (args.len <= 1) {
- std.debug.print("expected command argument\n\n{}", .{usage});
- process.exit(1);
- }
-
- const cmd = args[1];
- const cmd_args = args[2..];
- if (mem.eql(u8, cmd, "build-exe")) {
- return buildOutputType(gpa, arena, cmd_args, .Exe);
- } else if (mem.eql(u8, cmd, "build-lib")) {
- return buildOutputType(gpa, arena, cmd_args, .Lib);
- } else if (mem.eql(u8, cmd, "build-obj")) {
- return buildOutputType(gpa, arena, cmd_args, .Obj);
- } else if (mem.eql(u8, cmd, "fmt")) {
- return cmdFmt(gpa, cmd_args);
- } else if (mem.eql(u8, cmd, "targets")) {
- const info = try std.zig.system.NativeTargetInfo.detect(arena, .{});
- const stdout = io.getStdOut().outStream();
- return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
- } else if (mem.eql(u8, cmd, "version")) {
- try std.io.getStdOut().writeAll(build_options.version ++ "\n");
- } else if (mem.eql(u8, cmd, "env")) {
- try @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().outStream());
- } else if (mem.eql(u8, cmd, "zen")) {
- try io.getStdOut().writeAll(info_zen);
- } else if (mem.eql(u8, cmd, "help")) {
- try io.getStdOut().writeAll(usage);
- } else {
- std.debug.print("unknown command: {}\n\n{}", .{ args[1], usage });
- process.exit(1);
- }
-}
-
-const usage_build_generic =
- \\Usage: zig build-exe [files]
- \\ zig build-lib [files]
- \\ zig build-obj [files]
- \\
- \\Supported file types:
- \\ .zig Zig source code
- \\ .zir Zig Intermediate Representation code
- \\ (planned) .o ELF object file
- \\ (planned) .o MACH-O (macOS) object file
- \\ (planned) .obj COFF (Windows) object file
- \\ (planned) .lib COFF (Windows) static library
- \\ (planned) .a ELF static library
- \\ (planned) .so ELF shared object (dynamic link)
- \\ (planned) .dll Windows Dynamic Link Library
- \\ (planned) .dylib MACH-O (macOS) dynamic library
- \\ (planned) .s Target-specific assembly source code
- \\ (planned) .S Assembly with C preprocessor (requires LLVM extensions)
- \\ (planned) .c C source code (requires LLVM extensions)
- \\ (planned) .cpp C++ source code (requires LLVM extensions)
- \\ Other C++ extensions: .C .cc .cxx
- \\
- \\General Options:
- \\ -h, --help Print this help and exit
- \\ --watch Enable compiler REPL
- \\ --color [auto|off|on] Enable or disable colored error messages
- \\ -femit-bin[=path] (default) output machine code
- \\ -fno-emit-bin Do not output machine code
- \\
- \\Compile Options:
- \\ -target [name] -- see the targets command
- \\ -mcpu [cpu] Specify target CPU and feature set
- \\ --name [name] Override output name
- \\ --mode [mode] Set the build mode
- \\ Debug (default) optimizations off, safety on
- \\ ReleaseFast Optimizations on, safety off
- \\ ReleaseSafe Optimizations on, safety on
- \\ ReleaseSmall Optimize for small binary, safety off
- \\ --dynamic Force output to be dynamically linked
- \\ --strip Exclude debug symbols
- \\ -ofmt=[mode] Override target object format
- \\ elf Executable and Linking Format
- \\ c Compile to C source code
- \\ wasm WebAssembly
- \\ pe Portable Executable (Windows)
- \\ coff (planned) Common Object File Format (Windows)
- \\ macho (planned) macOS relocatables
- \\ hex (planned) Intel IHEX
- \\ raw (planned) Dump machine code directly
- \\
- \\Link Options:
- \\ -l[lib], --library [lib] Link against system library
- \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
- \\ --version [ver] Dynamic library semver
- \\
- \\Debug Options (Zig Compiler Development):
- \\ -ftime-report Print timing diagnostics
- \\ --debug-tokenize verbose tokenization
- \\ --debug-ast-tree verbose parsing into an AST (tree view)
- \\ --debug-ast-fmt verbose parsing into an AST (render source)
- \\ --debug-ir verbose Zig IR
- \\ --debug-link verbose linking
- \\ --debug-codegen verbose machine code generation
- \\
-;
-
-const Emit = union(enum) {
- no,
- yes_default_path,
- yes: []const u8,
-};
-
-fn buildOutputType(
- gpa: *Allocator,
- arena: *Allocator,
- args: []const []const u8,
- output_mode: std.builtin.OutputMode,
-) !void {
- var color: Color = .Auto;
- var build_mode: std.builtin.Mode = .Debug;
- var provided_name: ?[]const u8 = null;
- var link_mode: ?std.builtin.LinkMode = null;
- var root_src_file: ?[]const u8 = null;
- var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 };
- var strip = false;
- var watch = false;
- var debug_tokenize = false;
- var debug_ast_tree = false;
- var debug_ast_fmt = false;
- var debug_link = false;
- var debug_ir = false;
- var debug_codegen = false;
- var time_report = false;
- var emit_bin: Emit = .yes_default_path;
- var emit_zir: Emit = .no;
- var target_arch_os_abi: []const u8 = "native";
- var target_mcpu: ?[]const u8 = null;
- var target_dynamic_linker: ?[]const u8 = null;
- var target_ofmt: ?[]const u8 = null;
-
- var system_libs = std.ArrayList([]const u8).init(gpa);
- defer system_libs.deinit();
-
- {
- var i: usize = 0;
- while (i < args.len) : (i += 1) {
- const arg = args[i];
- if (mem.startsWith(u8, arg, "-")) {
- if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
- try io.getStdOut().writeAll(usage_build_generic);
- process.exit(0);
- } else if (mem.eql(u8, arg, "--color")) {
- if (i + 1 >= args.len) {
- std.debug.print("expected [auto|on|off] after --color\n", .{});
- process.exit(1);
- }
- i += 1;
- const next_arg = args[i];
- if (mem.eql(u8, next_arg, "auto")) {
- color = .Auto;
- } else if (mem.eql(u8, next_arg, "on")) {
- color = .On;
- } else if (mem.eql(u8, next_arg, "off")) {
- color = .Off;
- } else {
- std.debug.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
- process.exit(1);
- }
- } else if (mem.eql(u8, arg, "--mode")) {
- if (i + 1 >= args.len) {
- std.debug.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n", .{});
- process.exit(1);
- }
- i += 1;
- const next_arg = args[i];
- if (mem.eql(u8, next_arg, "Debug")) {
- build_mode = .Debug;
- } else if (mem.eql(u8, next_arg, "ReleaseSafe")) {
- build_mode = .ReleaseSafe;
- } else if (mem.eql(u8, next_arg, "ReleaseFast")) {
- build_mode = .ReleaseFast;
- } else if (mem.eql(u8, next_arg, "ReleaseSmall")) {
- build_mode = .ReleaseSmall;
- } else {
- std.debug.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'\n", .{next_arg});
- process.exit(1);
- }
- } else if (mem.eql(u8, arg, "--name")) {
- if (i + 1 >= args.len) {
- std.debug.print("expected parameter after --name\n", .{});
- process.exit(1);
- }
- i += 1;
- provided_name = args[i];
- } else if (mem.eql(u8, arg, "--library")) {
- if (i + 1 >= args.len) {
- std.debug.print("expected parameter after --library\n", .{});
- process.exit(1);
- }
- i += 1;
- try system_libs.append(args[i]);
- } else if (mem.eql(u8, arg, "--version")) {
- if (i + 1 >= args.len) {
- std.debug.print("expected parameter after --version\n", .{});
- process.exit(1);
- }
- i += 1;
- version = std.builtin.Version.parse(args[i]) catch |err| {
- std.debug.print("unable to parse --version '{}': {}\n", .{ args[i], @errorName(err) });
- process.exit(1);
- };
- } else if (mem.eql(u8, arg, "-target")) {
- if (i + 1 >= args.len) {
- std.debug.print("expected parameter after -target\n", .{});
- process.exit(1);
- }
- i += 1;
- target_arch_os_abi = args[i];
- } else if (mem.eql(u8, arg, "-mcpu")) {
- if (i + 1 >= args.len) {
- std.debug.print("expected parameter after -mcpu\n", .{});
- process.exit(1);
- }
- i += 1;
- target_mcpu = args[i];
- } else if (mem.startsWith(u8, arg, "-ofmt=")) {
- target_ofmt = arg["-ofmt=".len..];
- } else if (mem.startsWith(u8, arg, "-mcpu=")) {
- target_mcpu = arg["-mcpu=".len..];
- } else if (mem.eql(u8, arg, "--dynamic-linker")) {
- if (i + 1 >= args.len) {
- std.debug.print("expected parameter after --dynamic-linker\n", .{});
- process.exit(1);
- }
- i += 1;
- target_dynamic_linker = args[i];
- } else if (mem.eql(u8, arg, "--watch")) {
- watch = true;
- } else if (mem.eql(u8, arg, "-ftime-report")) {
- time_report = true;
- } else if (mem.eql(u8, arg, "-femit-bin")) {
- emit_bin = .yes_default_path;
- } else if (mem.startsWith(u8, arg, "-femit-bin=")) {
- emit_bin = .{ .yes = arg["-femit-bin=".len..] };
- } else if (mem.eql(u8, arg, "-fno-emit-bin")) {
- emit_bin = .no;
- } else if (mem.eql(u8, arg, "-femit-zir")) {
- emit_zir = .yes_default_path;
- } else if (mem.startsWith(u8, arg, "-femit-zir=")) {
- emit_zir = .{ .yes = arg["-femit-zir=".len..] };
- } else if (mem.eql(u8, arg, "-fno-emit-zir")) {
- emit_zir = .no;
- } else if (mem.eql(u8, arg, "-dynamic")) {
- link_mode = .Dynamic;
- } else if (mem.eql(u8, arg, "-static")) {
- link_mode = .Static;
- } else if (mem.eql(u8, arg, "--strip")) {
- strip = true;
- } else if (mem.eql(u8, arg, "--debug-tokenize")) {
- debug_tokenize = true;
- } else if (mem.eql(u8, arg, "--debug-ast-tree")) {
- debug_ast_tree = true;
- } else if (mem.eql(u8, arg, "--debug-ast-fmt")) {
- debug_ast_fmt = true;
- } else if (mem.eql(u8, arg, "--debug-link")) {
- debug_link = true;
- } else if (mem.eql(u8, arg, "--debug-ir")) {
- debug_ir = true;
- } else if (mem.eql(u8, arg, "--debug-codegen")) {
- debug_codegen = true;
- } else if (mem.startsWith(u8, arg, "-l")) {
- try system_libs.append(arg[2..]);
- } else {
- std.debug.print("unrecognized parameter: '{}'\n", .{arg});
- process.exit(1);
- }
- } else if (mem.endsWith(u8, arg, ".s") or mem.endsWith(u8, arg, ".S")) {
- std.debug.print("assembly files not supported yet\n", .{});
- process.exit(1);
- } else if (mem.endsWith(u8, arg, ".o") or
- mem.endsWith(u8, arg, ".obj") or
- mem.endsWith(u8, arg, ".a") or
- mem.endsWith(u8, arg, ".lib"))
- {
- std.debug.print("object files and static libraries not supported yet\n", .{});
- process.exit(1);
- } else if (mem.endsWith(u8, arg, ".c") or
- mem.endsWith(u8, arg, ".cpp"))
- {
- std.debug.print("compilation of C and C++ source code requires LLVM extensions which are not implemented yet\n", .{});
- process.exit(1);
- } else if (mem.endsWith(u8, arg, ".so") or
- mem.endsWith(u8, arg, ".dylib") or
- mem.endsWith(u8, arg, ".dll"))
- {
- std.debug.print("linking against dynamic libraries not yet supported\n", .{});
- process.exit(1);
- } else if (mem.endsWith(u8, arg, ".zig") or mem.endsWith(u8, arg, ".zir")) {
- if (root_src_file) |other| {
- std.debug.print("found another zig file '{}' after root source file '{}'\n", .{ arg, other });
- process.exit(1);
- } else {
- root_src_file = arg;
- }
- } else {
- std.debug.print("unrecognized file extension of parameter '{}'\n", .{arg});
- }
- }
- }
-
- const root_name = if (provided_name) |n| n else blk: {
- if (root_src_file) |file| {
- const basename = fs.path.basename(file);
- var it = mem.split(basename, ".");
- break :blk it.next() orelse basename;
- } else {
- std.debug.print("--name [name] not provided and unable to infer\n", .{});
- process.exit(1);
- }
- };
-
- if (system_libs.items.len != 0) {
- std.debug.print("linking against system libraries not yet supported\n", .{});
- process.exit(1);
- }
-
- var diags: std.zig.CrossTarget.ParseOptions.Diagnostics = .{};
- const cross_target = std.zig.CrossTarget.parse(.{
- .arch_os_abi = target_arch_os_abi,
- .cpu_features = target_mcpu,
- .dynamic_linker = target_dynamic_linker,
- .diagnostics = &diags,
- }) catch |err| switch (err) {
- error.UnknownCpuModel => {
- std.debug.print("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{
- diags.cpu_name.?,
- @tagName(diags.arch.?),
- });
- for (diags.arch.?.allCpuModels()) |cpu| {
- std.debug.print(" {}\n", .{cpu.name});
- }
- process.exit(1);
- },
- error.UnknownCpuFeature => {
- std.debug.print(
- \\Unknown CPU feature: '{}'
- \\Available CPU features for architecture '{}':
- \\
- , .{
- diags.unknown_feature_name,
- @tagName(diags.arch.?),
- });
- for (diags.arch.?.allFeaturesList()) |feature| {
- std.debug.print(" {}: {}\n", .{ feature.name, feature.description });
- }
- process.exit(1);
- },
- else => |e| return e,
- };
-
- var target_info = try std.zig.system.NativeTargetInfo.detect(gpa, cross_target);
- if (target_info.cpu_detection_unimplemented) {
- // TODO We want to just use detected_info.target but implementing
- // CPU model & feature detection is todo so here we rely on LLVM.
- std.debug.print("CPU features detection is not yet available for this system without LLVM extensions\n", .{});
- process.exit(1);
- }
-
- const src_path = root_src_file orelse {
- std.debug.print("expected at least one file argument", .{});
- process.exit(1);
- };
-
- const object_format: ?std.Target.ObjectFormat = blk: {
- const ofmt = target_ofmt orelse break :blk null;
- if (mem.eql(u8, ofmt, "elf")) {
- break :blk .elf;
- } else if (mem.eql(u8, ofmt, "c")) {
- break :blk .c;
- } else if (mem.eql(u8, ofmt, "coff")) {
- break :blk .coff;
- } else if (mem.eql(u8, ofmt, "pe")) {
- break :blk .pe;
- } else if (mem.eql(u8, ofmt, "macho")) {
- break :blk .macho;
- } else if (mem.eql(u8, ofmt, "wasm")) {
- break :blk .wasm;
- } else if (mem.eql(u8, ofmt, "hex")) {
- break :blk .hex;
- } else if (mem.eql(u8, ofmt, "raw")) {
- break :blk .raw;
- } else {
- std.debug.print("unsupported object format: {}", .{ofmt});
- process.exit(1);
- }
- };
-
- const bin_path = switch (emit_bin) {
- .no => {
- std.debug.print("-fno-emit-bin not supported yet", .{});
- process.exit(1);
- },
- .yes_default_path => if (object_format != null and object_format.? == .c)
- try std.fmt.allocPrint(arena, "{}.c", .{root_name})
- else
- try std.zig.binNameAlloc(arena, root_name, target_info.target, output_mode, link_mode),
-
- .yes => |p| p,
- };
-
- const zir_out_path: ?[]const u8 = switch (emit_zir) {
- .no => null,
- .yes_default_path => blk: {
- if (root_src_file) |rsf| {
- if (mem.endsWith(u8, rsf, ".zir")) {
- break :blk try std.fmt.allocPrint(arena, "{}.out.zir", .{root_name});
- }
- }
- break :blk try std.fmt.allocPrint(arena, "{}.zir", .{root_name});
- },
- .yes => |p| p,
- };
-
- const root_pkg = try Package.create(gpa, fs.cwd(), ".", src_path);
- defer root_pkg.destroy();
-
- var module = try Module.init(gpa, .{
- .root_name = root_name,
- .target = target_info.target,
- .output_mode = output_mode,
- .root_pkg = root_pkg,
- .bin_file_dir = fs.cwd(),
- .bin_file_path = bin_path,
- .link_mode = link_mode,
- .object_format = object_format,
- .optimize_mode = build_mode,
- .keep_source_files_loaded = zir_out_path != null,
- });
- defer module.deinit();
-
- const stdin = std.io.getStdIn().inStream();
- const stderr = std.io.getStdErr().outStream();
- var repl_buf: [1024]u8 = undefined;
-
- try updateModule(gpa, &module, zir_out_path);
-
- while (watch) {
- try stderr.print("🦎 ", .{});
- if (output_mode == .Exe) {
- try module.makeBinFileExecutable();
- }
- if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| {
- try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)});
- continue;
- }) |line| {
- const actual_line = mem.trimRight(u8, line, "\r\n ");
-
- if (mem.eql(u8, actual_line, "update")) {
- if (output_mode == .Exe) {
- try module.makeBinFileWritable();
- }
- try updateModule(gpa, &module, zir_out_path);
- } else if (mem.eql(u8, actual_line, "exit")) {
- break;
- } else if (mem.eql(u8, actual_line, "help")) {
- try stderr.writeAll(repl_help);
- } else {
- try stderr.print("unknown command: {}\n", .{actual_line});
- }
- } else {
- break;
- }
- }
-}
-
-fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !void {
- var timer = try std.time.Timer.start();
- try module.update();
- const update_nanos = timer.read();
-
- var errors = try module.getAllErrorsAlloc();
- defer errors.deinit(module.gpa);
-
- if (errors.list.len != 0) {
- for (errors.list) |full_err_msg| {
- std.debug.print("{}:{}:{}: error: {}\n", .{
- full_err_msg.src_path,
- full_err_msg.line + 1,
- full_err_msg.column + 1,
- full_err_msg.msg,
- });
- }
- } else {
- std.log.scoped(.compiler).info("Update completed in {} ms\n", .{update_nanos / std.time.ns_per_ms});
- }
-
- if (zir_out_path) |zop| {
- var new_zir_module = try zir.emit(gpa, module.*);
- defer new_zir_module.deinit(gpa);
-
- const baf = try io.BufferedAtomicFile.create(gpa, fs.cwd(), zop, .{});
- defer baf.destroy();
-
- try new_zir_module.writeToStream(gpa, baf.stream());
-
- try baf.finish();
- }
-}
-
-const repl_help =
- \\Commands:
- \\ update Detect changes to source files and update output files.
- \\ help Print this text
- \\ exit Quit this repl
- \\
-;
-
-pub const usage_fmt =
- \\usage: zig fmt [file]...
- \\
- \\ Formats the input files and modifies them in-place.
- \\ Arguments can be files or directories, which are searched
- \\ recursively.
- \\
- \\Options:
- \\ --help Print this help and exit
- \\ --color [auto|off|on] Enable or disable colored error messages
- \\ --stdin Format code from stdin; output to stdout
- \\ --check List non-conforming files and exit with an error
- \\ if the list is non-empty
- \\
- \\
-;
-
-const Fmt = struct {
- seen: SeenMap,
- any_error: bool,
- color: Color,
- gpa: *Allocator,
- out_buffer: std.ArrayList(u8),
-
- const SeenMap = std.AutoHashMap(fs.File.INode, void);
-};
-
-pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
- const stderr_file = io.getStdErr();
- var color: Color = .Auto;
- var stdin_flag: bool = false;
- var check_flag: bool = false;
- var input_files = ArrayList([]const u8).init(gpa);
-
- {
- var i: usize = 0;
- while (i < args.len) : (i += 1) {
- const arg = args[i];
- if (mem.startsWith(u8, arg, "-")) {
- if (mem.eql(u8, arg, "--help")) {
- const stdout = io.getStdOut().outStream();
- try stdout.writeAll(usage_fmt);
- process.exit(0);
- } else if (mem.eql(u8, arg, "--color")) {
- if (i + 1 >= args.len) {
- std.debug.print("expected [auto|on|off] after --color\n", .{});
- process.exit(1);
- }
- i += 1;
- const next_arg = args[i];
- if (mem.eql(u8, next_arg, "auto")) {
- color = .Auto;
- } else if (mem.eql(u8, next_arg, "on")) {
- color = .On;
- } else if (mem.eql(u8, next_arg, "off")) {
- color = .Off;
- } else {
- std.debug.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
- process.exit(1);
- }
- } else if (mem.eql(u8, arg, "--stdin")) {
- stdin_flag = true;
- } else if (mem.eql(u8, arg, "--check")) {
- check_flag = true;
- } else {
- std.debug.print("unrecognized parameter: '{}'", .{arg});
- process.exit(1);
- }
- } else {
- try input_files.append(arg);
- }
- }
- }
-
- if (stdin_flag) {
- if (input_files.items.len != 0) {
- std.debug.print("cannot use --stdin with positional arguments\n", .{});
- process.exit(1);
- }
-
- const stdin = io.getStdIn().inStream();
-
- const source_code = try stdin.readAllAlloc(gpa, max_src_size);
- defer gpa.free(source_code);
-
- const tree = std.zig.parse(gpa, source_code) catch |err| {
- std.debug.print("error parsing stdin: {}\n", .{err});
- process.exit(1);
- };
- defer tree.deinit();
-
- for (tree.errors) |parse_error| {
- try printErrMsgToFile(gpa, parse_error, tree, "", stderr_file, color);
- }
- if (tree.errors.len != 0) {
- process.exit(1);
- }
- if (check_flag) {
- const anything_changed = try std.zig.render(gpa, io.null_out_stream, tree);
- const code = if (anything_changed) @as(u8, 1) else @as(u8, 0);
- process.exit(code);
- }
-
- const stdout = io.getStdOut().outStream();
- _ = try std.zig.render(gpa, stdout, tree);
- return;
- }
-
- if (input_files.items.len == 0) {
- std.debug.print("expected at least one source file argument\n", .{});
- process.exit(1);
- }
-
- var fmt = Fmt{
- .gpa = gpa,
- .seen = Fmt.SeenMap.init(gpa),
- .any_error = false,
- .color = color,
- .out_buffer = std.ArrayList(u8).init(gpa),
- };
- defer fmt.seen.deinit();
- defer fmt.out_buffer.deinit();
-
- for (input_files.span()) |file_path| {
- // Get the real path here to avoid Windows failing on relative file paths with . or .. in them.
- const real_path = fs.realpathAlloc(gpa, file_path) catch |err| {
- std.debug.print("unable to open '{}': {}\n", .{ file_path, err });
- process.exit(1);
- };
- defer gpa.free(real_path);
-
- try fmtPath(&fmt, file_path, check_flag, fs.cwd(), real_path);
- }
- if (fmt.any_error) {
- process.exit(1);
- }
-}
-
-const FmtError = error{
- SystemResources,
- OperationAborted,
- IoPending,
- BrokenPipe,
- Unexpected,
- WouldBlock,
- FileClosed,
- DestinationAddressRequired,
- DiskQuota,
- FileTooBig,
- InputOutput,
- NoSpaceLeft,
- AccessDenied,
- OutOfMemory,
- RenameAcrossMountPoints,
- ReadOnlyFileSystem,
- LinkQuotaExceeded,
- FileBusy,
- EndOfStream,
- Unseekable,
- NotOpenForWriting,
-} || fs.File.OpenError;
-
-fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void {
- fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) {
- error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path),
- else => {
- std.debug.print("unable to format '{}': {}\n", .{ file_path, err });
- fmt.any_error = true;
- return;
- },
- };
-}
-
-fn fmtPathDir(
- fmt: *Fmt,
- file_path: []const u8,
- check_mode: bool,
- parent_dir: fs.Dir,
- parent_sub_path: []const u8,
-) FmtError!void {
- var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true });
- defer dir.close();
-
- const stat = try dir.stat();
- if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
-
- var dir_it = dir.iterate();
- while (try dir_it.next()) |entry| {
- const is_dir = entry.kind == .Directory;
- if (is_dir or mem.endsWith(u8, entry.name, ".zig")) {
- const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
- defer fmt.gpa.free(full_path);
-
- if (is_dir) {
- try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);
- } else {
- fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| {
- std.debug.print("unable to format '{}': {}\n", .{ full_path, err });
- fmt.any_error = true;
- return;
- };
- }
- }
- }
-}
-
-fn fmtPathFile(
- fmt: *Fmt,
- file_path: []const u8,
- check_mode: bool,
- dir: fs.Dir,
- sub_path: []const u8,
-) FmtError!void {
- const source_file = try dir.openFile(sub_path, .{});
- var file_closed = false;
- errdefer if (!file_closed) source_file.close();
-
- const stat = try source_file.stat();
-
- if (stat.kind == .Directory)
- return error.IsDir;
-
- const source_code = source_file.readToEndAllocOptions(
- fmt.gpa,
- max_src_size,
- stat.size,
- @alignOf(u8),
- null,
- ) catch |err| switch (err) {
- error.ConnectionResetByPeer => unreachable,
- error.ConnectionTimedOut => unreachable,
- error.NotOpenForReading => unreachable,
- else => |e| return e,
- };
- source_file.close();
- file_closed = true;
- defer fmt.gpa.free(source_code);
-
- // Add to set after no longer possible to get error.IsDir.
- if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
-
- const tree = try std.zig.parse(fmt.gpa, source_code);
- defer tree.deinit();
-
- for (tree.errors) |parse_error| {
- try printErrMsgToFile(fmt.gpa, parse_error, tree, file_path, std.io.getStdErr(), fmt.color);
- }
- if (tree.errors.len != 0) {
- fmt.any_error = true;
- return;
- }
-
- if (check_mode) {
- const anything_changed = try std.zig.render(fmt.gpa, io.null_out_stream, tree);
- if (anything_changed) {
- std.debug.print("{}\n", .{file_path});
- fmt.any_error = true;
- }
- } else {
- // As a heuristic, we make enough capacity for the same as the input source.
- try fmt.out_buffer.ensureCapacity(source_code.len);
- fmt.out_buffer.items.len = 0;
- const writer = fmt.out_buffer.writer();
- const anything_changed = try std.zig.render(fmt.gpa, writer, tree);
- if (!anything_changed)
- return; // Good thing we didn't waste any file system access on this.
-
- var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode });
- defer af.deinit();
-
- try af.file.writeAll(fmt.out_buffer.items);
- try af.finish();
- std.debug.print("{}\n", .{file_path});
- }
-}
-
-fn printErrMsgToFile(
- gpa: *mem.Allocator,
- parse_error: ast.Error,
- tree: *ast.Tree,
- path: []const u8,
- file: fs.File,
- color: Color,
-) !void {
- const color_on = switch (color) {
- .Auto => file.isTty(),
- .On => true,
- .Off => false,
- };
- const lok_token = parse_error.loc();
- const span_first = lok_token;
- const span_last = lok_token;
-
- const first_token = tree.token_locs[span_first];
- const last_token = tree.token_locs[span_last];
- const start_loc = tree.tokenLocationLoc(0, first_token);
- const end_loc = tree.tokenLocationLoc(first_token.end, last_token);
-
- var text_buf = std.ArrayList(u8).init(gpa);
- defer text_buf.deinit();
- const out_stream = text_buf.outStream();
- try parse_error.render(tree.token_ids, out_stream);
- const text = text_buf.span();
-
- const stream = file.outStream();
- try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text });
-
- if (!color_on) return;
-
- // Print \r and \t as one space each so that column counts line up
- for (tree.source[start_loc.line_start..start_loc.line_end]) |byte| {
- try stream.writeByte(switch (byte) {
- '\r', '\t' => ' ',
- else => byte,
- });
- }
- try stream.writeByte('\n');
- try stream.writeByteNTimes(' ', start_loc.column);
- try stream.writeByteNTimes('~', last_token.end - first_token.start);
- try stream.writeByte('\n');
-}
-
-pub const info_zen =
- \\
- \\ * Communicate intent precisely.
- \\ * Edge cases matter.
- \\ * Favor reading code over writing code.
- \\ * Only one obvious way to do things.
- \\ * Runtime crashes are better than bugs.
- \\ * Compile errors are better than runtime crashes.
- \\ * Incremental improvements.
- \\ * Avoid local maximums.
- \\ * Reduce the amount one must remember.
- \\ * Minimize energy spent on coding style.
- \\ * Resource deallocation must succeed.
- \\ * Together we serve the users.
- \\
- \\
-;
diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig
deleted file mode 100644
index ac4d89bb21..0000000000
--- a/src-self-hosted/stage2.zig
+++ /dev/null
@@ -1,1300 +0,0 @@
-// This is Zig code that is used by both stage1 and stage2.
-// The prototypes in src/userland.h must match these definitions.
-
-const std = @import("std");
-const io = std.io;
-const mem = std.mem;
-const fs = std.fs;
-const process = std.process;
-const Allocator = mem.Allocator;
-const ArrayList = std.ArrayList;
-const ArrayListSentineled = std.ArrayListSentineled;
-const Target = std.Target;
-const CrossTarget = std.zig.CrossTarget;
-const self_hosted_main = @import("main.zig");
-const DepTokenizer = @import("dep_tokenizer.zig").Tokenizer;
-const assert = std.debug.assert;
-const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
-
-var stderr_file: fs.File = undefined;
-var stderr: fs.File.OutStream = undefined;
-var stdout: fs.File.OutStream = undefined;
-
-comptime {
- _ = @import("dep_tokenizer.zig");
-}
-
-// ABI warning
-export fn stage2_zen(ptr: *[*]const u8, len: *usize) void {
- const info_zen = @import("main.zig").info_zen;
- ptr.* = info_zen;
- len.* = info_zen.len;
-}
-
-// ABI warning
-export fn stage2_panic(ptr: [*]const u8, len: usize) void {
- @panic(ptr[0..len]);
-}
-
-// ABI warning
-const Error = extern enum {
- None,
- OutOfMemory,
- InvalidFormat,
- SemanticAnalyzeFail,
- AccessDenied,
- Interrupted,
- SystemResources,
- FileNotFound,
- FileSystem,
- FileTooBig,
- DivByZero,
- Overflow,
- PathAlreadyExists,
- Unexpected,
- ExactDivRemainder,
- NegativeDenominator,
- ShiftedOutOneBits,
- CCompileErrors,
- EndOfFile,
- IsDir,
- NotDir,
- UnsupportedOperatingSystem,
- SharingViolation,
- PipeBusy,
- PrimitiveTypeNotFound,
- CacheUnavailable,
- PathTooLong,
- CCompilerCannotFindFile,
- NoCCompilerInstalled,
- ReadingDepFile,
- InvalidDepFile,
- MissingArchitecture,
- MissingOperatingSystem,
- UnknownArchitecture,
- UnknownOperatingSystem,
- UnknownABI,
- InvalidFilename,
- DiskQuota,
- DiskSpace,
- UnexpectedWriteFailure,
- UnexpectedSeekFailure,
- UnexpectedFileTruncationFailure,
- Unimplemented,
- OperationAborted,
- BrokenPipe,
- NoSpaceLeft,
- NotLazy,
- IsAsync,
- ImportOutsidePkgPath,
- UnknownCpuModel,
- UnknownCpuFeature,
- InvalidCpuFeatures,
- InvalidLlvmCpuFeaturesFormat,
- UnknownApplicationBinaryInterface,
- ASTUnitFailure,
- BadPathName,
- SymLinkLoop,
- ProcessFdQuotaExceeded,
- SystemFdQuotaExceeded,
- NoDevice,
- DeviceBusy,
- UnableToSpawnCCompiler,
- CCompilerExitCode,
- CCompilerCrashed,
- CCompilerCannotFindHeaders,
- LibCRuntimeNotFound,
- LibCStdLibHeaderNotFound,
- LibCKernel32LibNotFound,
- UnsupportedArchitecture,
- WindowsSdkNotFound,
- UnknownDynamicLinkerPath,
- TargetHasNoDynamicLinker,
- InvalidAbiVersion,
- InvalidOperatingSystemVersion,
- UnknownClangOption,
- NestedResponseFile,
- ZigIsTheCCompiler,
- FileBusy,
- Locked,
-};
-
-const FILE = std.c.FILE;
-const ast = std.zig.ast;
-const translate_c = @import("translate_c.zig");
-
-/// Args should have a null terminating last arg.
-export fn stage2_translate_c(
- out_ast: **ast.Tree,
- out_errors_ptr: *[*]translate_c.ClangErrMsg,
- out_errors_len: *usize,
- args_begin: [*]?[*]const u8,
- args_end: [*]?[*]const u8,
- resources_path: [*:0]const u8,
-) Error {
- var errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{};
- out_ast.* = translate_c.translate(std.heap.c_allocator, args_begin, args_end, &errors, resources_path) catch |err| switch (err) {
- error.SemanticAnalyzeFail => {
- out_errors_ptr.* = errors.ptr;
- out_errors_len.* = errors.len;
- return .CCompileErrors;
- },
- error.ASTUnitFailure => return .ASTUnitFailure,
- error.OutOfMemory => return .OutOfMemory,
- };
- return .None;
-}
-
-export fn stage2_free_clang_errors(errors_ptr: [*]translate_c.ClangErrMsg, errors_len: usize) void {
- translate_c.freeErrors(errors_ptr[0..errors_len]);
-}
-
-export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error {
- const c_out_stream = std.io.cOutStream(output_file);
- _ = std.zig.render(std.heap.c_allocator, c_out_stream, tree) catch |e| switch (e) {
- error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode
- error.NotOpenForWriting => unreachable,
- error.SystemResources => return .SystemResources,
- error.OperationAborted => return .OperationAborted,
- error.BrokenPipe => return .BrokenPipe,
- error.DiskQuota => return .DiskQuota,
- error.FileTooBig => return .FileTooBig,
- error.NoSpaceLeft => return .NoSpaceLeft,
- error.AccessDenied => return .AccessDenied,
- error.OutOfMemory => return .OutOfMemory,
- error.Unexpected => return .Unexpected,
- error.InputOutput => return .FileSystem,
- };
- return .None;
-}
-
-export fn stage2_fmt(argc: c_int, argv: [*]const [*:0]const u8) c_int {
- if (std.debug.runtime_safety) {
- fmtMain(argc, argv) catch unreachable;
- } else {
- fmtMain(argc, argv) catch |e| {
- std.debug.warn("{}\n", .{@errorName(e)});
- return -1;
- };
- }
- return 0;
-}
-
-fn argvToArrayList(allocator: *Allocator, argc: c_int, argv: [*]const [*:0]const u8) !ArrayList([]const u8) {
- var args_list = std.ArrayList([]const u8).init(allocator);
- const argc_usize = @intCast(usize, argc);
- var arg_i: usize = 0;
- while (arg_i < argc_usize) : (arg_i += 1) {
- try args_list.append(mem.spanZ(argv[arg_i]));
- }
-
- return args_list;
-}
-
-fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void {
- const allocator = std.heap.c_allocator;
-
- var args_list = try argvToArrayList(allocator, argc, argv);
- defer args_list.deinit();
-
- const args = args_list.span()[2..];
- return self_hosted_main.cmdFmt(allocator, args);
-}
-
-export fn stage2_DepTokenizer_init(input: [*]const u8, len: usize) stage2_DepTokenizer {
- const t = std.heap.c_allocator.create(DepTokenizer) catch @panic("failed to create .d tokenizer");
- t.* = DepTokenizer.init(std.heap.c_allocator, input[0..len]);
- return stage2_DepTokenizer{
- .handle = t,
- };
-}
-
-export fn stage2_DepTokenizer_deinit(self: *stage2_DepTokenizer) void {
- self.handle.deinit();
-}
-
-export fn stage2_DepTokenizer_next(self: *stage2_DepTokenizer) stage2_DepNextResult {
- const otoken = self.handle.next() catch {
- const textz = std.ArrayListSentineled(u8, 0).init(&self.handle.arena.allocator, self.handle.error_text) catch @panic("failed to create .d tokenizer error text");
- return stage2_DepNextResult{
- .type_id = .error_,
- .textz = textz.span().ptr,
- };
- };
- const token = otoken orelse {
- return stage2_DepNextResult{
- .type_id = .null_,
- .textz = undefined,
- };
- };
- const textz = std.ArrayListSentineled(u8, 0).init(&self.handle.arena.allocator, token.bytes) catch @panic("failed to create .d tokenizer token text");
- return stage2_DepNextResult{
- .type_id = switch (token.id) {
- .target => .target,
- .prereq => .prereq,
- },
- .textz = textz.span().ptr,
- };
-}
-
-const stage2_DepTokenizer = extern struct {
- handle: *DepTokenizer,
-};
-
-const stage2_DepNextResult = extern struct {
- type_id: TypeId,
-
- // when type_id == error --> error text
- // when type_id == null --> undefined
- // when type_id == target --> target pathname
- // when type_id == prereq --> prereq pathname
- textz: [*]const u8,
-
- const TypeId = extern enum {
- error_,
- null_,
- target,
- prereq,
- };
-};
-
-// ABI warning
-export fn stage2_attach_segfault_handler() void {
- if (std.debug.runtime_safety and std.debug.have_segfault_handling_support) {
- std.debug.attachSegfaultHandler();
- }
-}
-
-// ABI warning
-export fn stage2_progress_create() *std.Progress {
- const ptr = std.heap.c_allocator.create(std.Progress) catch @panic("out of memory");
- ptr.* = std.Progress{};
- return ptr;
-}
-
-// ABI warning
-export fn stage2_progress_destroy(progress: *std.Progress) void {
- std.heap.c_allocator.destroy(progress);
-}
-
-// ABI warning
-export fn stage2_progress_start_root(
- progress: *std.Progress,
- name_ptr: [*]const u8,
- name_len: usize,
- estimated_total_items: usize,
-) *std.Progress.Node {
- return progress.start(
- name_ptr[0..name_len],
- if (estimated_total_items == 0) null else estimated_total_items,
- ) catch @panic("timer unsupported");
-}
-
-// ABI warning
-export fn stage2_progress_disable_tty(progress: *std.Progress) void {
- progress.terminal = null;
-}
-
-// ABI warning
-export fn stage2_progress_start(
- node: *std.Progress.Node,
- name_ptr: [*]const u8,
- name_len: usize,
- estimated_total_items: usize,
-) *std.Progress.Node {
- const child_node = std.heap.c_allocator.create(std.Progress.Node) catch @panic("out of memory");
- child_node.* = node.start(
- name_ptr[0..name_len],
- if (estimated_total_items == 0) null else estimated_total_items,
- );
- child_node.activate();
- return child_node;
-}
-
-// ABI warning
-export fn stage2_progress_end(node: *std.Progress.Node) void {
- node.end();
- if (&node.context.root != node) {
- std.heap.c_allocator.destroy(node);
- }
-}
-
-// ABI warning
-export fn stage2_progress_complete_one(node: *std.Progress.Node) void {
- node.completeOne();
-}
-
-// ABI warning
-export fn stage2_progress_update_node(node: *std.Progress.Node, done_count: usize, total_count: usize) void {
- node.completed_items = done_count;
- node.estimated_total_items = total_count;
- node.activate();
- node.context.maybeRefresh();
-}
-
-fn detectNativeCpuWithLLVM(
- arch: Target.Cpu.Arch,
- llvm_cpu_name_z: ?[*:0]const u8,
- llvm_cpu_features_opt: ?[*:0]const u8,
-) !Target.Cpu {
- var result = Target.Cpu.baseline(arch);
-
- if (llvm_cpu_name_z) |cpu_name_z| {
- const llvm_cpu_name = mem.spanZ(cpu_name_z);
-
- for (arch.allCpuModels()) |model| {
- const this_llvm_name = model.llvm_name orelse continue;
- if (mem.eql(u8, this_llvm_name, llvm_cpu_name)) {
- // Here we use the non-dependencies-populated set,
- // so that subtracting features later in this function
- // affect the prepopulated set.
- result = Target.Cpu{
- .arch = arch,
- .model = model,
- .features = model.features,
- };
- break;
- }
- }
- }
-
- const all_features = arch.allFeaturesList();
-
- if (llvm_cpu_features_opt) |llvm_cpu_features| {
- var it = mem.tokenize(mem.spanZ(llvm_cpu_features), ",");
- while (it.next()) |decorated_llvm_feat| {
- var op: enum {
- add,
- sub,
- } = undefined;
- var llvm_feat: []const u8 = undefined;
- if (mem.startsWith(u8, decorated_llvm_feat, "+")) {
- op = .add;
- llvm_feat = decorated_llvm_feat[1..];
- } else if (mem.startsWith(u8, decorated_llvm_feat, "-")) {
- op = .sub;
- llvm_feat = decorated_llvm_feat[1..];
- } else {
- return error.InvalidLlvmCpuFeaturesFormat;
- }
- for (all_features) |feature, index_usize| {
- const this_llvm_name = feature.llvm_name orelse continue;
- if (mem.eql(u8, llvm_feat, this_llvm_name)) {
- const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize);
- switch (op) {
- .add => result.features.addFeature(index),
- .sub => result.features.removeFeature(index),
- }
- break;
- }
- }
- }
- }
-
- result.features.populateDependencies(all_features);
- return result;
-}
-
-export fn stage2_env(argc: c_int, argv: [*]const [*:0]const u8) c_int {
- const allocator = std.heap.c_allocator;
-
- var args_list = argvToArrayList(allocator, argc, argv) catch |err| {
- std.debug.print("unable to parse arguments: {}\n", .{@errorName(err)});
- return -1;
- };
- defer args_list.deinit();
-
- const args = args_list.span()[2..];
-
- @import("print_env.zig").cmdEnv(allocator, args, std.io.getStdOut().outStream()) catch |err| {
- std.debug.print("unable to print info: {}\n", .{@errorName(err)});
- return -1;
- };
-
- return 0;
-}
-
-// ABI warning
-export fn stage2_cmd_targets(
- zig_triple: ?[*:0]const u8,
- mcpu: ?[*:0]const u8,
- dynamic_linker: ?[*:0]const u8,
-) c_int {
- cmdTargets(zig_triple, mcpu, dynamic_linker) catch |err| {
- std.debug.warn("unable to list targets: {}\n", .{@errorName(err)});
- return -1;
- };
- return 0;
-}
-
-fn cmdTargets(
- zig_triple_oz: ?[*:0]const u8,
- mcpu_oz: ?[*:0]const u8,
- dynamic_linker_oz: ?[*:0]const u8,
-) !void {
- const cross_target = try stage2CrossTarget(zig_triple_oz, mcpu_oz, dynamic_linker_oz);
- var dynamic_linker: ?[*:0]u8 = null;
- const target = try crossTargetToTarget(cross_target, &dynamic_linker);
- return @import("print_targets.zig").cmdTargets(
- std.heap.c_allocator,
- &[0][]u8{},
- std.io.getStdOut().outStream(),
- target,
- );
-}
-
-// ABI warning
-export fn stage2_target_parse(
- target: *Stage2Target,
- zig_triple: ?[*:0]const u8,
- mcpu: ?[*:0]const u8,
- dynamic_linker: ?[*:0]const u8,
-) Error {
- stage2TargetParse(target, zig_triple, mcpu, dynamic_linker) catch |err| switch (err) {
- error.OutOfMemory => return .OutOfMemory,
- error.UnknownArchitecture => return .UnknownArchitecture,
- error.UnknownOperatingSystem => return .UnknownOperatingSystem,
- error.UnknownApplicationBinaryInterface => return .UnknownApplicationBinaryInterface,
- error.MissingOperatingSystem => return .MissingOperatingSystem,
- error.InvalidLlvmCpuFeaturesFormat => return .InvalidLlvmCpuFeaturesFormat,
- error.UnexpectedExtraField => return .SemanticAnalyzeFail,
- error.InvalidAbiVersion => return .InvalidAbiVersion,
- error.InvalidOperatingSystemVersion => return .InvalidOperatingSystemVersion,
- error.FileSystem => return .FileSystem,
- error.SymLinkLoop => return .SymLinkLoop,
- error.SystemResources => return .SystemResources,
- error.ProcessFdQuotaExceeded => return .ProcessFdQuotaExceeded,
- error.SystemFdQuotaExceeded => return .SystemFdQuotaExceeded,
- error.DeviceBusy => return .DeviceBusy,
- };
- return .None;
-}
-
-fn stage2CrossTarget(
- zig_triple_oz: ?[*:0]const u8,
- mcpu_oz: ?[*:0]const u8,
- dynamic_linker_oz: ?[*:0]const u8,
-) !CrossTarget {
- const mcpu = mem.spanZ(mcpu_oz);
- const dynamic_linker = mem.spanZ(dynamic_linker_oz);
- var diags: CrossTarget.ParseOptions.Diagnostics = .{};
- const target: CrossTarget = CrossTarget.parse(.{
- .arch_os_abi = mem.spanZ(zig_triple_oz) orelse "native",
- .cpu_features = mcpu,
- .dynamic_linker = dynamic_linker,
- .diagnostics = &diags,
- }) catch |err| switch (err) {
- error.UnknownCpuModel => {
- std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{
- diags.cpu_name.?,
- @tagName(diags.arch.?),
- });
- for (diags.arch.?.allCpuModels()) |cpu| {
- std.debug.warn(" {}\n", .{cpu.name});
- }
- process.exit(1);
- },
- error.UnknownCpuFeature => {
- std.debug.warn(
- \\Unknown CPU feature: '{}'
- \\Available CPU features for architecture '{}':
- \\
- , .{
- diags.unknown_feature_name,
- @tagName(diags.arch.?),
- });
- for (diags.arch.?.allFeaturesList()) |feature| {
- std.debug.warn(" {}: {}\n", .{ feature.name, feature.description });
- }
- process.exit(1);
- },
- else => |e| return e,
- };
-
- return target;
-}
-
-fn stage2TargetParse(
- stage1_target: *Stage2Target,
- zig_triple_oz: ?[*:0]const u8,
- mcpu_oz: ?[*:0]const u8,
- dynamic_linker_oz: ?[*:0]const u8,
-) !void {
- const target = try stage2CrossTarget(zig_triple_oz, mcpu_oz, dynamic_linker_oz);
- try stage1_target.fromTarget(target);
-}
-
-// ABI warning
-const Stage2LibCInstallation = extern struct {
- include_dir: [*]const u8,
- include_dir_len: usize,
- sys_include_dir: [*]const u8,
- sys_include_dir_len: usize,
- crt_dir: [*]const u8,
- crt_dir_len: usize,
- msvc_lib_dir: [*]const u8,
- msvc_lib_dir_len: usize,
- kernel32_lib_dir: [*]const u8,
- kernel32_lib_dir_len: usize,
-
- fn initFromStage2(self: *Stage2LibCInstallation, libc: LibCInstallation) void {
- if (libc.include_dir) |s| {
- self.include_dir = s.ptr;
- self.include_dir_len = s.len;
- } else {
- self.include_dir = "";
- self.include_dir_len = 0;
- }
- if (libc.sys_include_dir) |s| {
- self.sys_include_dir = s.ptr;
- self.sys_include_dir_len = s.len;
- } else {
- self.sys_include_dir = "";
- self.sys_include_dir_len = 0;
- }
- if (libc.crt_dir) |s| {
- self.crt_dir = s.ptr;
- self.crt_dir_len = s.len;
- } else {
- self.crt_dir = "";
- self.crt_dir_len = 0;
- }
- if (libc.msvc_lib_dir) |s| {
- self.msvc_lib_dir = s.ptr;
- self.msvc_lib_dir_len = s.len;
- } else {
- self.msvc_lib_dir = "";
- self.msvc_lib_dir_len = 0;
- }
- if (libc.kernel32_lib_dir) |s| {
- self.kernel32_lib_dir = s.ptr;
- self.kernel32_lib_dir_len = s.len;
- } else {
- self.kernel32_lib_dir = "";
- self.kernel32_lib_dir_len = 0;
- }
- }
-
- fn toStage2(self: Stage2LibCInstallation) LibCInstallation {
- var libc: LibCInstallation = .{};
- if (self.include_dir_len != 0) {
- libc.include_dir = self.include_dir[0..self.include_dir_len];
- }
- if (self.sys_include_dir_len != 0) {
- libc.sys_include_dir = self.sys_include_dir[0..self.sys_include_dir_len];
- }
- if (self.crt_dir_len != 0) {
- libc.crt_dir = self.crt_dir[0..self.crt_dir_len];
- }
- if (self.msvc_lib_dir_len != 0) {
- libc.msvc_lib_dir = self.msvc_lib_dir[0..self.msvc_lib_dir_len];
- }
- if (self.kernel32_lib_dir_len != 0) {
- libc.kernel32_lib_dir = self.kernel32_lib_dir[0..self.kernel32_lib_dir_len];
- }
- return libc;
- }
-};
-
-// ABI warning
-export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [*:0]const u8) Error {
- const libc_file = mem.spanZ(libc_file_z);
- var libc = LibCInstallation.parse(std.heap.c_allocator, libc_file) catch |err| switch (err) {
- error.ParseError => return .SemanticAnalyzeFail,
- error.FileTooBig => return .FileTooBig,
- error.InputOutput => return .FileSystem,
- error.NoSpaceLeft => return .NoSpaceLeft,
- error.AccessDenied => return .AccessDenied,
- error.BrokenPipe => return .BrokenPipe,
- error.SystemResources => return .SystemResources,
- error.OperationAborted => return .OperationAborted,
- error.WouldBlock => unreachable,
- error.NotOpenForReading => unreachable,
- error.Unexpected => return .Unexpected,
- error.IsDir => return .IsDir,
- error.ConnectionResetByPeer => unreachable,
- error.ConnectionTimedOut => unreachable,
- error.OutOfMemory => return .OutOfMemory,
- error.Unseekable => unreachable,
- error.SharingViolation => return .SharingViolation,
- error.PathAlreadyExists => unreachable,
- error.FileNotFound => return .FileNotFound,
- error.PipeBusy => return .PipeBusy,
- error.NameTooLong => return .PathTooLong,
- error.InvalidUtf8 => return .BadPathName,
- error.BadPathName => return .BadPathName,
- error.SymLinkLoop => return .SymLinkLoop,
- error.ProcessFdQuotaExceeded => return .ProcessFdQuotaExceeded,
- error.SystemFdQuotaExceeded => return .SystemFdQuotaExceeded,
- error.NoDevice => return .NoDevice,
- error.NotDir => return .NotDir,
- error.DeviceBusy => return .DeviceBusy,
- error.FileLocksNotSupported => unreachable,
- };
- stage1_libc.initFromStage2(libc);
- return .None;
-}
-
-// ABI warning
-export fn stage2_libc_find_native(stage1_libc: *Stage2LibCInstallation) Error {
- var libc = LibCInstallation.findNative(.{
- .allocator = std.heap.c_allocator,
- .verbose = true,
- }) catch |err| switch (err) {
- error.OutOfMemory => return .OutOfMemory,
- error.FileSystem => return .FileSystem,
- error.UnableToSpawnCCompiler => return .UnableToSpawnCCompiler,
- error.CCompilerExitCode => return .CCompilerExitCode,
- error.CCompilerCrashed => return .CCompilerCrashed,
- error.CCompilerCannotFindHeaders => return .CCompilerCannotFindHeaders,
- error.LibCRuntimeNotFound => return .LibCRuntimeNotFound,
- error.LibCStdLibHeaderNotFound => return .LibCStdLibHeaderNotFound,
- error.LibCKernel32LibNotFound => return .LibCKernel32LibNotFound,
- error.UnsupportedArchitecture => return .UnsupportedArchitecture,
- error.WindowsSdkNotFound => return .WindowsSdkNotFound,
- error.ZigIsTheCCompiler => return .ZigIsTheCCompiler,
- };
- stage1_libc.initFromStage2(libc);
- return .None;
-}
-
-// ABI warning
-export fn stage2_libc_render(stage1_libc: *Stage2LibCInstallation, output_file: *FILE) Error {
- var libc = stage1_libc.toStage2();
- const c_out_stream = std.io.cOutStream(output_file);
- libc.render(c_out_stream) catch |err| switch (err) {
- error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode
- error.NotOpenForWriting => unreachable,
- error.SystemResources => return .SystemResources,
- error.OperationAborted => return .OperationAborted,
- error.BrokenPipe => return .BrokenPipe,
- error.DiskQuota => return .DiskQuota,
- error.FileTooBig => return .FileTooBig,
- error.NoSpaceLeft => return .NoSpaceLeft,
- error.AccessDenied => return .AccessDenied,
- error.Unexpected => return .Unexpected,
- error.InputOutput => return .FileSystem,
- };
- return .None;
-}
-
-// ABI warning
-const Stage2Target = extern struct {
- arch: c_int,
- vendor: c_int,
-
- abi: c_int,
- os: c_int,
-
- is_native_os: bool,
- is_native_cpu: bool,
-
- glibc_or_darwin_version: ?*Stage2SemVer,
-
- llvm_cpu_name: ?[*:0]const u8,
- llvm_cpu_features: ?[*:0]const u8,
- cpu_builtin_str: ?[*:0]const u8,
- cache_hash: ?[*:0]const u8,
- cache_hash_len: usize,
- os_builtin_str: ?[*:0]const u8,
-
- dynamic_linker: ?[*:0]const u8,
- standard_dynamic_linker_path: ?[*:0]const u8,
-
- llvm_cpu_features_asm_ptr: [*]const [*:0]const u8,
- llvm_cpu_features_asm_len: usize,
-
- fn fromTarget(self: *Stage2Target, cross_target: CrossTarget) !void {
- const allocator = std.heap.c_allocator;
-
- var dynamic_linker: ?[*:0]u8 = null;
- const target = try crossTargetToTarget(cross_target, &dynamic_linker);
-
- var cache_hash = try std.ArrayListSentineled(u8, 0).allocPrint(allocator, "{}\n{}\n", .{
- target.cpu.model.name,
- target.cpu.features.asBytes(),
- });
- defer cache_hash.deinit();
-
- const generic_arch_name = target.cpu.arch.genericName();
- var cpu_builtin_str_buffer = try std.ArrayListSentineled(u8, 0).allocPrint(allocator,
- \\Cpu{{
- \\ .arch = .{},
- \\ .model = &Target.{}.cpu.{},
- \\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{
- \\
- , .{
- @tagName(target.cpu.arch),
- generic_arch_name,
- target.cpu.model.name,
- generic_arch_name,
- generic_arch_name,
- });
- defer cpu_builtin_str_buffer.deinit();
-
- var llvm_features_buffer = try std.ArrayListSentineled(u8, 0).initSize(allocator, 0);
- defer llvm_features_buffer.deinit();
-
- // Unfortunately we have to do the work twice, because Clang does not support
- // the same command line parameters for CPU features when assembling code as it does
- // when compiling C code.
- var asm_features_list = std.ArrayList([*:0]const u8).init(allocator);
- defer asm_features_list.deinit();
-
- for (target.cpu.arch.allFeaturesList()) |feature, index_usize| {
- const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize);
- const is_enabled = target.cpu.features.isEnabled(index);
-
- if (feature.llvm_name) |llvm_name| {
- const plus_or_minus = "-+"[@boolToInt(is_enabled)];
- try llvm_features_buffer.append(plus_or_minus);
- try llvm_features_buffer.appendSlice(llvm_name);
- try llvm_features_buffer.appendSlice(",");
- }
-
- if (is_enabled) {
- // TODO some kind of "zig identifier escape" function rather than
- // unconditionally using @"" syntax
- try cpu_builtin_str_buffer.appendSlice(" .@\"");
- try cpu_builtin_str_buffer.appendSlice(feature.name);
- try cpu_builtin_str_buffer.appendSlice("\",\n");
- }
- }
-
- switch (target.cpu.arch) {
- .riscv32, .riscv64 => {
- if (std.Target.riscv.featureSetHas(target.cpu.features, .relax)) {
- try asm_features_list.append("-mrelax");
- } else {
- try asm_features_list.append("-mno-relax");
- }
- },
- else => {
- // TODO
- // 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.
- },
- }
-
- try cpu_builtin_str_buffer.appendSlice(
- \\ }),
- \\};
- \\
- );
-
- assert(mem.endsWith(u8, llvm_features_buffer.span(), ","));
- llvm_features_buffer.shrink(llvm_features_buffer.len() - 1);
-
- var os_builtin_str_buffer = try std.ArrayListSentineled(u8, 0).allocPrint(allocator,
- \\Os{{
- \\ .tag = .{},
- \\ .version_range = .{{
- , .{@tagName(target.os.tag)});
- defer os_builtin_str_buffer.deinit();
-
- // We'll re-use the OS version range builtin string for the cache hash.
- const os_builtin_str_ver_start_index = os_builtin_str_buffer.len();
-
- @setEvalBranchQuota(2000);
- switch (target.os.tag) {
- .freestanding,
- .ananas,
- .cloudabi,
- .dragonfly,
- .fuchsia,
- .ios,
- .kfreebsd,
- .lv2,
- .solaris,
- .haiku,
- .minix,
- .rtems,
- .nacl,
- .cnk,
- .aix,
- .cuda,
- .nvcl,
- .amdhsa,
- .ps4,
- .elfiamcu,
- .tvos,
- .watchos,
- .mesa3d,
- .contiki,
- .amdpal,
- .hermit,
- .hurd,
- .wasi,
- .emscripten,
- .uefi,
- .other,
- => try os_builtin_str_buffer.appendSlice(" .none = {} }\n"),
-
- .freebsd,
- .macosx,
- .netbsd,
- .openbsd,
- => try os_builtin_str_buffer.outStream().print(
- \\ .semver = .{{
- \\ .min = .{{
- \\ .major = {},
- \\ .minor = {},
- \\ .patch = {},
- \\ }},
- \\ .max = .{{
- \\ .major = {},
- \\ .minor = {},
- \\ .patch = {},
- \\ }},
- \\ }}}},
- \\
- , .{
- target.os.version_range.semver.min.major,
- target.os.version_range.semver.min.minor,
- target.os.version_range.semver.min.patch,
-
- target.os.version_range.semver.max.major,
- target.os.version_range.semver.max.minor,
- target.os.version_range.semver.max.patch,
- }),
-
- .linux => try os_builtin_str_buffer.outStream().print(
- \\ .linux = .{{
- \\ .range = .{{
- \\ .min = .{{
- \\ .major = {},
- \\ .minor = {},
- \\ .patch = {},
- \\ }},
- \\ .max = .{{
- \\ .major = {},
- \\ .minor = {},
- \\ .patch = {},
- \\ }},
- \\ }},
- \\ .glibc = .{{
- \\ .major = {},
- \\ .minor = {},
- \\ .patch = {},
- \\ }},
- \\ }}}},
- \\
- , .{
- target.os.version_range.linux.range.min.major,
- target.os.version_range.linux.range.min.minor,
- target.os.version_range.linux.range.min.patch,
-
- target.os.version_range.linux.range.max.major,
- target.os.version_range.linux.range.max.minor,
- target.os.version_range.linux.range.max.patch,
-
- target.os.version_range.linux.glibc.major,
- target.os.version_range.linux.glibc.minor,
- target.os.version_range.linux.glibc.patch,
- }),
-
- .windows => try os_builtin_str_buffer.outStream().print(
- \\ .windows = .{{
- \\ .min = {s},
- \\ .max = {s},
- \\ }}}},
- \\
- , .{
- target.os.version_range.windows.min,
- target.os.version_range.windows.max,
- }),
- }
- try os_builtin_str_buffer.appendSlice("};\n");
-
- try cache_hash.appendSlice(
- os_builtin_str_buffer.span()[os_builtin_str_ver_start_index..os_builtin_str_buffer.len()],
- );
-
- const glibc_or_darwin_version = blk: {
- if (target.isGnuLibC()) {
- const stage1_glibc = try std.heap.c_allocator.create(Stage2SemVer);
- const stage2_glibc = target.os.version_range.linux.glibc;
- stage1_glibc.* = .{
- .major = stage2_glibc.major,
- .minor = stage2_glibc.minor,
- .patch = stage2_glibc.patch,
- };
- break :blk stage1_glibc;
- } else if (target.isDarwin()) {
- const stage1_semver = try std.heap.c_allocator.create(Stage2SemVer);
- const stage2_semver = target.os.version_range.semver.min;
- stage1_semver.* = .{
- .major = stage2_semver.major,
- .minor = stage2_semver.minor,
- .patch = stage2_semver.patch,
- };
- break :blk stage1_semver;
- } else {
- break :blk null;
- }
- };
-
- const std_dl = target.standardDynamicLinkerPath();
- const std_dl_z = if (std_dl.get()) |dl|
- (try mem.dupeZ(std.heap.c_allocator, u8, dl)).ptr
- else
- null;
-
- const cache_hash_slice = cache_hash.toOwnedSlice();
- const asm_features = asm_features_list.toOwnedSlice();
- self.* = .{
- .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch
- .vendor = 0,
- .os = @enumToInt(target.os.tag),
- .abi = @enumToInt(target.abi),
- .llvm_cpu_name = if (target.cpu.model.llvm_name) |s| s.ptr else null,
- .llvm_cpu_features = llvm_features_buffer.toOwnedSlice().ptr,
- .llvm_cpu_features_asm_ptr = asm_features.ptr,
- .llvm_cpu_features_asm_len = asm_features.len,
- .cpu_builtin_str = cpu_builtin_str_buffer.toOwnedSlice().ptr,
- .os_builtin_str = os_builtin_str_buffer.toOwnedSlice().ptr,
- .cache_hash = cache_hash_slice.ptr,
- .cache_hash_len = cache_hash_slice.len,
- .is_native_os = cross_target.isNativeOs(),
- .is_native_cpu = cross_target.isNativeCpu(),
- .glibc_or_darwin_version = glibc_or_darwin_version,
- .dynamic_linker = dynamic_linker,
- .standard_dynamic_linker_path = std_dl_z,
- };
- }
-};
-
-fn enumInt(comptime Enum: type, int: c_int) Enum {
- return @intToEnum(Enum, @intCast(@TagType(Enum), int));
-}
-
-fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) !Target {
- var info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator, cross_target);
- if (info.cpu_detection_unimplemented) {
- // TODO We want to just use detected_info.target but implementing
- // CPU model & feature detection is todo so here we rely on LLVM.
- const llvm = @import("llvm.zig");
- const llvm_cpu_name = llvm.GetHostCPUName();
- const llvm_cpu_features = llvm.GetNativeFeatures();
- const arch = std.Target.current.cpu.arch;
- info.target.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features);
- cross_target.updateCpuFeatures(&info.target.cpu.features);
- info.target.cpu.arch = cross_target.getCpuArch();
- }
- if (info.dynamic_linker.get()) |dl| {
- dynamic_linker_ptr.* = try mem.dupeZ(std.heap.c_allocator, u8, dl);
- } else {
- dynamic_linker_ptr.* = null;
- }
- return info.target;
-}
-
-// ABI warning
-const Stage2SemVer = extern struct {
- major: u32,
- minor: u32,
- patch: u32,
-};
-
-// ABI warning
-const Stage2NativePaths = extern struct {
- include_dirs_ptr: [*][*:0]u8,
- include_dirs_len: usize,
- lib_dirs_ptr: [*][*:0]u8,
- lib_dirs_len: usize,
- rpaths_ptr: [*][*:0]u8,
- rpaths_len: usize,
- warnings_ptr: [*][*:0]u8,
- warnings_len: usize,
-};
-// ABI warning
-export fn stage2_detect_native_paths(stage1_paths: *Stage2NativePaths) Error {
- stage2DetectNativePaths(stage1_paths) catch |err| switch (err) {
- error.OutOfMemory => return .OutOfMemory,
- };
- return .None;
-}
-
-fn stage2DetectNativePaths(stage1_paths: *Stage2NativePaths) !void {
- var paths = try std.zig.system.NativePaths.detect(std.heap.c_allocator);
- errdefer paths.deinit();
-
- try convertSlice(paths.include_dirs.span(), &stage1_paths.include_dirs_ptr, &stage1_paths.include_dirs_len);
- try convertSlice(paths.lib_dirs.span(), &stage1_paths.lib_dirs_ptr, &stage1_paths.lib_dirs_len);
- try convertSlice(paths.rpaths.span(), &stage1_paths.rpaths_ptr, &stage1_paths.rpaths_len);
- try convertSlice(paths.warnings.span(), &stage1_paths.warnings_ptr, &stage1_paths.warnings_len);
-}
-
-fn convertSlice(slice: [][:0]u8, ptr: *[*][*:0]u8, len: *usize) !void {
- len.* = slice.len;
- const new_slice = try std.heap.c_allocator.alloc([*:0]u8, slice.len);
- for (slice) |item, i| {
- new_slice[i] = item.ptr;
- }
- ptr.* = new_slice.ptr;
-}
-
-const clang_args = @import("clang_options.zig").list;
-
-// ABI warning
-pub const ClangArgIterator = extern struct {
- has_next: bool,
- zig_equivalent: ZigEquivalent,
- only_arg: [*:0]const u8,
- second_arg: [*:0]const u8,
- other_args_ptr: [*]const [*:0]const u8,
- other_args_len: usize,
- argv_ptr: [*]const [*:0]const u8,
- argv_len: usize,
- next_index: usize,
- root_args: ?*Args,
-
- // ABI warning
- pub const ZigEquivalent = extern enum {
- target,
- o,
- c,
- other,
- positional,
- l,
- ignore,
- driver_punt,
- pic,
- no_pic,
- nostdlib,
- nostdlib_cpp,
- shared,
- rdynamic,
- wl,
- pp_or_asm,
- optimize,
- debug,
- sanitize,
- linker_script,
- verbose_cmds,
- for_linker,
- linker_input_z,
- lib_dir,
- mcpu,
- dep_file,
- framework_dir,
- framework,
- nostdlibinc,
- };
-
- const Args = struct {
- next_index: usize,
- argv_ptr: [*]const [*:0]const u8,
- argv_len: usize,
- };
-
- pub fn init(argv: []const [*:0]const u8) ClangArgIterator {
- return .{
- .next_index = 2, // `zig cc foo` this points to `foo`
- .has_next = argv.len > 2,
- .zig_equivalent = undefined,
- .only_arg = undefined,
- .second_arg = undefined,
- .other_args_ptr = undefined,
- .other_args_len = undefined,
- .argv_ptr = argv.ptr,
- .argv_len = argv.len,
- .root_args = null,
- };
- }
-
- pub fn next(self: *ClangArgIterator) !void {
- assert(self.has_next);
- assert(self.next_index < self.argv_len);
- // In this state we know that the parameter we are looking at is a root parameter
- // rather than an argument to a parameter.
- self.other_args_ptr = self.argv_ptr + self.next_index;
- self.other_args_len = 1; // We adjust this value below when necessary.
- var arg = mem.span(self.argv_ptr[self.next_index]);
- self.incrementArgIndex();
-
- if (mem.startsWith(u8, arg, "@")) {
- if (self.root_args != null) return error.NestedResponseFile;
-
- // This is a "compiler response file". We must parse the file and treat its
- // contents as command line parameters.
- const allocator = std.heap.c_allocator;
- const max_bytes = 10 * 1024 * 1024; // 10 MiB of command line arguments is a reasonable limit
- const resp_file_path = arg[1..];
- const resp_contents = fs.cwd().readFileAlloc(allocator, resp_file_path, max_bytes) catch |err| {
- std.debug.warn("unable to read response file '{}': {}\n", .{ resp_file_path, @errorName(err) });
- process.exit(1);
- };
- defer allocator.free(resp_contents);
- // TODO is there a specification for this file format? Let's find it and make this parsing more robust
- // at the very least I'm guessing this needs to handle quotes and `#` comments.
- var it = mem.tokenize(resp_contents, " \t\r\n");
- var resp_arg_list = std.ArrayList([*:0]const u8).init(allocator);
- defer resp_arg_list.deinit();
- {
- errdefer {
- for (resp_arg_list.span()) |item| {
- allocator.free(mem.span(item));
- }
- }
- while (it.next()) |token| {
- const dupe_token = try mem.dupeZ(allocator, u8, token);
- errdefer allocator.free(dupe_token);
- try resp_arg_list.append(dupe_token);
- }
- const args = try allocator.create(Args);
- errdefer allocator.destroy(args);
- args.* = .{
- .next_index = self.next_index,
- .argv_ptr = self.argv_ptr,
- .argv_len = self.argv_len,
- };
- self.root_args = args;
- }
- const resp_arg_slice = resp_arg_list.toOwnedSlice();
- self.next_index = 0;
- self.argv_ptr = resp_arg_slice.ptr;
- self.argv_len = resp_arg_slice.len;
-
- if (resp_arg_slice.len == 0) {
- self.resolveRespFileArgs();
- return;
- }
-
- self.has_next = true;
- self.other_args_ptr = self.argv_ptr + self.next_index;
- self.other_args_len = 1; // We adjust this value below when necessary.
- arg = mem.span(self.argv_ptr[self.next_index]);
- self.incrementArgIndex();
- }
- if (!mem.startsWith(u8, arg, "-")) {
- self.zig_equivalent = .positional;
- self.only_arg = arg.ptr;
- return;
- }
-
- find_clang_arg: for (clang_args) |clang_arg| switch (clang_arg.syntax) {
- .flag => {
- const prefix_len = clang_arg.matchEql(arg);
- if (prefix_len > 0) {
- self.zig_equivalent = clang_arg.zig_equivalent;
- self.only_arg = arg.ptr + prefix_len;
-
- break :find_clang_arg;
- }
- },
- .joined, .comma_joined => {
- // joined example: --target=foo
- // comma_joined example: -Wl,-soname,libsoundio.so.2
- const prefix_len = clang_arg.matchStartsWith(arg);
- if (prefix_len != 0) {
- self.zig_equivalent = clang_arg.zig_equivalent;
- self.only_arg = arg.ptr + prefix_len; // This will skip over the "--target=" part.
-
- break :find_clang_arg;
- }
- },
- .joined_or_separate => {
- // Examples: `-lfoo`, `-l foo`
- const prefix_len = clang_arg.matchStartsWith(arg);
- if (prefix_len == arg.len) {
- if (self.next_index >= self.argv_len) {
- std.debug.warn("Expected parameter after '{}'\n", .{arg});
- process.exit(1);
- }
- self.only_arg = self.argv_ptr[self.next_index];
- self.incrementArgIndex();
- self.other_args_len += 1;
- self.zig_equivalent = clang_arg.zig_equivalent;
-
- break :find_clang_arg;
- } else if (prefix_len != 0) {
- self.zig_equivalent = clang_arg.zig_equivalent;
- self.only_arg = arg.ptr + prefix_len;
-
- break :find_clang_arg;
- }
- },
- .joined_and_separate => {
- // Example: `-Xopenmp-target=riscv64-linux-unknown foo`
- const prefix_len = clang_arg.matchStartsWith(arg);
- if (prefix_len != 0) {
- self.only_arg = arg.ptr + prefix_len;
- if (self.next_index >= self.argv_len) {
- std.debug.warn("Expected parameter after '{}'\n", .{arg});
- process.exit(1);
- }
- self.second_arg = self.argv_ptr[self.next_index];
- self.incrementArgIndex();
- self.other_args_len += 1;
- self.zig_equivalent = clang_arg.zig_equivalent;
- break :find_clang_arg;
- }
- },
- .separate => if (clang_arg.matchEql(arg) > 0) {
- if (self.next_index >= self.argv_len) {
- std.debug.warn("Expected parameter after '{}'\n", .{arg});
- process.exit(1);
- }
- self.only_arg = self.argv_ptr[self.next_index];
- self.incrementArgIndex();
- self.other_args_len += 1;
- self.zig_equivalent = clang_arg.zig_equivalent;
- break :find_clang_arg;
- },
- .remaining_args_joined => {
- const prefix_len = clang_arg.matchStartsWith(arg);
- if (prefix_len != 0) {
- @panic("TODO");
- }
- },
- .multi_arg => if (clang_arg.matchEql(arg) > 0) {
- @panic("TODO");
- },
- }
- else {
- std.debug.warn("Unknown Clang option: '{}'\n", .{arg});
- process.exit(1);
- }
- }
-
- fn incrementArgIndex(self: *ClangArgIterator) void {
- self.next_index += 1;
- self.resolveRespFileArgs();
- }
-
- fn resolveRespFileArgs(self: *ClangArgIterator) void {
- const allocator = std.heap.c_allocator;
- if (self.next_index >= self.argv_len) {
- if (self.root_args) |root_args| {
- self.next_index = root_args.next_index;
- self.argv_ptr = root_args.argv_ptr;
- self.argv_len = root_args.argv_len;
-
- allocator.destroy(root_args);
- self.root_args = null;
- }
- if (self.next_index >= self.argv_len) {
- self.has_next = false;
- }
- }
- }
-};
-
-export fn stage2_clang_arg_iterator(
- result: *ClangArgIterator,
- argc: usize,
- argv: [*]const [*:0]const u8,
-) void {
- result.* = ClangArgIterator.init(argv[0..argc]);
-}
-
-export fn stage2_clang_arg_next(it: *ClangArgIterator) Error {
- it.next() catch |err| switch (err) {
- error.NestedResponseFile => return .NestedResponseFile,
- error.OutOfMemory => return .OutOfMemory,
- };
- return .None;
-}
-
-export const stage2_is_zig0 = false;
diff --git a/src/Cache.zig b/src/Cache.zig
new file mode 100644
index 0000000000..dff6f7e38e
--- /dev/null
+++ b/src/Cache.zig
@@ -0,0 +1,911 @@
+gpa: *Allocator,
+manifest_dir: fs.Dir,
+hash: HashHelper = .{},
+
+const Cache = @This();
+const std = @import("std");
+const crypto = std.crypto;
+const fs = std.fs;
+const assert = std.debug.assert;
+const testing = std.testing;
+const mem = std.mem;
+const fmt = std.fmt;
+const Allocator = std.mem.Allocator;
+
+/// Be sure to call `Manifest.deinit` after successful initialization.
+pub fn obtain(cache: *const Cache) Manifest {
+ return Manifest{
+ .cache = cache,
+ .hash = cache.hash,
+ .manifest_file = null,
+ .manifest_dirty = false,
+ .hex_digest = undefined,
+ };
+}
+
+/// This is 128 bits - Even with 2^54 cache entries, the probably of a collision would be under 10^-6
+pub const bin_digest_len = 16;
+pub const hex_digest_len = bin_digest_len * 2;
+
+const manifest_file_size_max = 50 * 1024 * 1024;
+
+/// The type used for hashing file contents. Currently, this is SipHash128(1, 3), because it
+/// provides enough collision resistance for the Manifest use cases, while being one of our
+/// fastest options right now.
+pub const Hasher = crypto.auth.siphash.SipHash128(1, 3);
+
+/// Initial state, that can be copied.
+pub const hasher_init: Hasher = Hasher.init(&[_]u8{0} ** Hasher.minimum_key_length);
+
+pub const File = struct {
+ path: ?[]const u8,
+ max_file_size: ?usize,
+ stat: fs.File.Stat,
+ bin_digest: [bin_digest_len]u8,
+ contents: ?[]const u8,
+
+ pub fn deinit(self: *File, allocator: *Allocator) void {
+ if (self.path) |owned_slice| {
+ allocator.free(owned_slice);
+ self.path = null;
+ }
+ if (self.contents) |contents| {
+ allocator.free(contents);
+ self.contents = null;
+ }
+ self.* = undefined;
+ }
+};
+
+pub const HashHelper = struct {
+ hasher: Hasher = hasher_init,
+
+ /// Record a slice of bytes as an dependency of the process being cached
+ pub fn addBytes(hh: *HashHelper, bytes: []const u8) void {
+ hh.hasher.update(mem.asBytes(&bytes.len));
+ hh.hasher.update(bytes);
+ }
+
+ pub fn addOptionalBytes(hh: *HashHelper, optional_bytes: ?[]const u8) void {
+ hh.add(optional_bytes != null);
+ hh.addBytes(optional_bytes orelse return);
+ }
+
+ pub fn addListOfBytes(hh: *HashHelper, list_of_bytes: []const []const u8) void {
+ hh.add(list_of_bytes.len);
+ for (list_of_bytes) |bytes| hh.addBytes(bytes);
+ }
+
+ pub fn addStringSet(hh: *HashHelper, hm: std.StringArrayHashMapUnmanaged(void)) void {
+ const entries = hm.items();
+ hh.add(entries.len);
+ for (entries) |entry| {
+ hh.addBytes(entry.key);
+ }
+ }
+
+ /// Convert the input value into bytes and record it as a dependency of the process being cached.
+ pub fn add(hh: *HashHelper, x: anytype) void {
+ switch (@TypeOf(x)) {
+ std.builtin.Version => {
+ hh.add(x.major);
+ hh.add(x.minor);
+ hh.add(x.patch);
+ },
+ std.Target.Os.TaggedVersionRange => {
+ switch (x) {
+ .linux => |linux| {
+ hh.add(linux.range.min);
+ hh.add(linux.range.max);
+ hh.add(linux.glibc);
+ },
+ .windows => |windows| {
+ hh.add(windows.min);
+ hh.add(windows.max);
+ },
+ .semver => |semver| {
+ hh.add(semver.min);
+ hh.add(semver.max);
+ },
+ .none => {},
+ }
+ },
+ else => switch (@typeInfo(@TypeOf(x))) {
+ .Bool, .Int, .Enum, .Array => hh.addBytes(mem.asBytes(&x)),
+ else => @compileError("unable to hash type " ++ @typeName(@TypeOf(x))),
+ },
+ }
+ }
+
+ pub fn addOptional(hh: *HashHelper, optional: anytype) void {
+ hh.add(optional != null);
+ hh.add(optional orelse return);
+ }
+
+ /// Returns a hex encoded hash of the inputs, without modifying state.
+ pub fn peek(hh: HashHelper) [hex_digest_len]u8 {
+ var copy = hh;
+ return copy.final();
+ }
+
+ pub fn peekBin(hh: HashHelper) [bin_digest_len]u8 {
+ var copy = hh;
+ var bin_digest: [bin_digest_len]u8 = undefined;
+ copy.hasher.final(&bin_digest);
+ return bin_digest;
+ }
+
+ /// Returns a hex encoded hash of the inputs, mutating the state of the hasher.
+ pub fn final(hh: *HashHelper) [hex_digest_len]u8 {
+ var bin_digest: [bin_digest_len]u8 = undefined;
+ hh.hasher.final(&bin_digest);
+
+ var out_digest: [hex_digest_len]u8 = undefined;
+ _ = std.fmt.bufPrint(&out_digest, "{x}", .{bin_digest}) catch unreachable;
+ return out_digest;
+ }
+};
+
+pub const Lock = struct {
+ manifest_file: fs.File,
+
+ pub fn release(lock: *Lock) void {
+ lock.manifest_file.close();
+ lock.* = undefined;
+ }
+};
+
+/// Manifest manages project-local `zig-cache` directories.
+/// This is not a general-purpose cache.
+/// It is designed to be fast and simple, not to withstand attacks using specially-crafted input.
+pub const Manifest = struct {
+ cache: *const Cache,
+ /// Current state for incremental hashing.
+ hash: HashHelper,
+ manifest_file: ?fs.File,
+ manifest_dirty: bool,
+ files: std.ArrayListUnmanaged(File) = .{},
+ hex_digest: [hex_digest_len]u8,
+
+ /// Add a file as a dependency of process being cached. When `hit` is
+ /// called, the file's contents will be checked to ensure that it matches
+ /// the contents from previous times.
+ ///
+ /// Max file size will be used to determine the amount of space to the file contents
+ /// are allowed to take up in memory. If max_file_size is null, then the contents
+ /// will not be loaded into memory.
+ ///
+ /// Returns the index of the entry in the `files` array list. You can use it
+ /// to access the contents of the file after calling `hit()` like so:
+ ///
+ /// ```
+ /// var file_contents = cache_hash.files.items[file_index].contents.?;
+ /// ```
+ pub fn addFile(self: *Manifest, file_path: []const u8, max_file_size: ?usize) !usize {
+ assert(self.manifest_file == null);
+
+ try self.files.ensureCapacity(self.cache.gpa, self.files.items.len + 1);
+ const resolved_path = try fs.path.resolve(self.cache.gpa, &[_][]const u8{file_path});
+
+ const idx = self.files.items.len;
+ self.files.addOneAssumeCapacity().* = .{
+ .path = resolved_path,
+ .contents = null,
+ .max_file_size = max_file_size,
+ .stat = undefined,
+ .bin_digest = undefined,
+ };
+
+ self.hash.addBytes(resolved_path);
+
+ return idx;
+ }
+
+ pub fn addOptionalFile(self: *Manifest, optional_file_path: ?[]const u8) !void {
+ self.hash.add(optional_file_path != null);
+ const file_path = optional_file_path orelse return;
+ _ = try self.addFile(file_path, null);
+ }
+
+ pub fn addListOfFiles(self: *Manifest, list_of_files: []const []const u8) !void {
+ self.hash.add(list_of_files.len);
+ for (list_of_files) |file_path| {
+ _ = try self.addFile(file_path, null);
+ }
+ }
+
+ /// Check the cache to see if the input exists in it. If it exists, returns `true`.
+ /// A hex encoding of its hash is available by calling `final`.
+ ///
+ /// This function will also acquire an exclusive lock to the manifest file. This means
+ /// that a process holding a Manifest will block any other process attempting to
+ /// acquire the lock.
+ ///
+ /// The lock on the manifest file is released when `deinit` is called. As another
+ /// option, one may call `toOwnedLock` to obtain a smaller object which can represent
+ /// the lock. `deinit` is safe to call whether or not `toOwnedLock` has been called.
+ pub fn hit(self: *Manifest) !bool {
+ assert(self.manifest_file == null);
+
+ const ext = ".txt";
+ var manifest_file_path: [self.hex_digest.len + ext.len]u8 = undefined;
+
+ var bin_digest: [bin_digest_len]u8 = undefined;
+ self.hash.hasher.final(&bin_digest);
+
+ _ = std.fmt.bufPrint(&self.hex_digest, "{x}", .{bin_digest}) catch unreachable;
+
+ self.hash.hasher = hasher_init;
+ self.hash.hasher.update(&bin_digest);
+
+ mem.copy(u8, &manifest_file_path, &self.hex_digest);
+ manifest_file_path[self.hex_digest.len..][0..ext.len].* = ext.*;
+
+ if (self.files.items.len != 0) {
+ self.manifest_file = try self.cache.manifest_dir.createFile(&manifest_file_path, .{
+ .read = true,
+ .truncate = false,
+ .lock = .Exclusive,
+ });
+ } else {
+ // If there are no file inputs, we check if the manifest file exists instead of
+ // comparing the hashes on the files used for the cached item
+ self.manifest_file = self.cache.manifest_dir.openFile(&manifest_file_path, .{
+ .read = true,
+ .write = true,
+ .lock = .Exclusive,
+ }) catch |err| switch (err) {
+ error.FileNotFound => {
+ self.manifest_dirty = true;
+ self.manifest_file = try self.cache.manifest_dir.createFile(&manifest_file_path, .{
+ .read = true,
+ .truncate = false,
+ .lock = .Exclusive,
+ });
+ return false;
+ },
+ else => |e| return e,
+ };
+ }
+
+ const file_contents = try self.manifest_file.?.inStream().readAllAlloc(self.cache.gpa, manifest_file_size_max);
+ defer self.cache.gpa.free(file_contents);
+
+ const input_file_count = self.files.items.len;
+ var any_file_changed = false;
+ var line_iter = mem.tokenize(file_contents, "\n");
+ var idx: usize = 0;
+ while (line_iter.next()) |line| {
+ defer idx += 1;
+
+ const cache_hash_file = if (idx < input_file_count) &self.files.items[idx] else blk: {
+ const new = try self.files.addOne(self.cache.gpa);
+ new.* = .{
+ .path = null,
+ .contents = null,
+ .max_file_size = null,
+ .stat = undefined,
+ .bin_digest = undefined,
+ };
+ break :blk new;
+ };
+
+ var iter = mem.tokenize(line, " ");
+ const size = iter.next() orelse return error.InvalidFormat;
+ const inode = iter.next() orelse return error.InvalidFormat;
+ const mtime_nsec_str = iter.next() orelse return error.InvalidFormat;
+ const digest_str = iter.next() orelse return error.InvalidFormat;
+ const file_path = iter.rest();
+
+ cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat;
+ cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat;
+ cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat;
+ std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat;
+
+ if (file_path.len == 0) {
+ return error.InvalidFormat;
+ }
+ if (cache_hash_file.path) |p| {
+ if (!mem.eql(u8, file_path, p)) {
+ return error.InvalidFormat;
+ }
+ }
+
+ if (cache_hash_file.path == null) {
+ cache_hash_file.path = try self.cache.gpa.dupe(u8, file_path);
+ }
+
+ const this_file = fs.cwd().openFile(cache_hash_file.path.?, .{ .read = true }) catch {
+ return error.CacheUnavailable;
+ };
+ defer this_file.close();
+
+ const actual_stat = try this_file.stat();
+ const size_match = actual_stat.size == cache_hash_file.stat.size;
+ const mtime_match = actual_stat.mtime == cache_hash_file.stat.mtime;
+ const inode_match = actual_stat.inode == cache_hash_file.stat.inode;
+
+ if (!size_match or !mtime_match or !inode_match) {
+ self.manifest_dirty = true;
+
+ cache_hash_file.stat = actual_stat;
+
+ if (isProblematicTimestamp(cache_hash_file.stat.mtime)) {
+ cache_hash_file.stat.mtime = 0;
+ cache_hash_file.stat.inode = 0;
+ }
+
+ var actual_digest: [bin_digest_len]u8 = undefined;
+ try hashFile(this_file, &actual_digest);
+
+ if (!mem.eql(u8, &cache_hash_file.bin_digest, &actual_digest)) {
+ cache_hash_file.bin_digest = actual_digest;
+ // keep going until we have the input file digests
+ any_file_changed = true;
+ }
+ }
+
+ if (!any_file_changed) {
+ self.hash.hasher.update(&cache_hash_file.bin_digest);
+ }
+ }
+
+ if (any_file_changed) {
+ // cache miss
+ // keep the manifest file open
+ self.unhit(bin_digest, input_file_count);
+ return false;
+ }
+
+ if (idx < input_file_count) {
+ self.manifest_dirty = true;
+ while (idx < input_file_count) : (idx += 1) {
+ const ch_file = &self.files.items[idx];
+ try self.populateFileHash(ch_file);
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ pub fn unhit(self: *Manifest, bin_digest: [bin_digest_len]u8, input_file_count: usize) void {
+ // Reset the hash.
+ self.hash.hasher = hasher_init;
+ self.hash.hasher.update(&bin_digest);
+
+ // Remove files not in the initial hash.
+ for (self.files.items[input_file_count..]) |*file| {
+ file.deinit(self.cache.gpa);
+ }
+ self.files.shrinkRetainingCapacity(input_file_count);
+
+ for (self.files.items) |file| {
+ self.hash.hasher.update(&file.bin_digest);
+ }
+ }
+
+ fn populateFileHash(self: *Manifest, ch_file: *File) !void {
+ const file = try fs.cwd().openFile(ch_file.path.?, .{});
+ defer file.close();
+
+ ch_file.stat = try file.stat();
+
+ if (isProblematicTimestamp(ch_file.stat.mtime)) {
+ ch_file.stat.mtime = 0;
+ ch_file.stat.inode = 0;
+ }
+
+ if (ch_file.max_file_size) |max_file_size| {
+ if (ch_file.stat.size > max_file_size) {
+ return error.FileTooBig;
+ }
+
+ const contents = try self.cache.gpa.alloc(u8, @intCast(usize, ch_file.stat.size));
+ errdefer self.cache.gpa.free(contents);
+
+ // Hash while reading from disk, to keep the contents in the cpu cache while
+ // doing hashing.
+ var hasher = hasher_init;
+ var off: usize = 0;
+ while (true) {
+ // give me everything you've got, captain
+ const bytes_read = try file.read(contents[off..]);
+ if (bytes_read == 0) break;
+ hasher.update(contents[off..][0..bytes_read]);
+ off += bytes_read;
+ }
+ hasher.final(&ch_file.bin_digest);
+
+ ch_file.contents = contents;
+ } else {
+ try hashFile(file, &ch_file.bin_digest);
+ }
+
+ self.hash.hasher.update(&ch_file.bin_digest);
+ }
+
+ /// Add a file as a dependency of process being cached, after the initial hash has been
+ /// calculated. This is useful for processes that don't know the all the files that
+ /// are depended on ahead of time. For example, a source file that can import other files
+ /// will need to be recompiled if the imported file is changed.
+ pub fn addFilePostFetch(self: *Manifest, file_path: []const u8, max_file_size: usize) ![]const u8 {
+ assert(self.manifest_file != null);
+
+ const resolved_path = try fs.path.resolve(self.cache.gpa, &[_][]const u8{file_path});
+ errdefer self.cache.gpa.free(resolved_path);
+
+ const new_ch_file = try self.files.addOne(self.cache.gpa);
+ new_ch_file.* = .{
+ .path = resolved_path,
+ .max_file_size = max_file_size,
+ .stat = undefined,
+ .bin_digest = undefined,
+ .contents = null,
+ };
+ errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1);
+
+ try self.populateFileHash(new_ch_file);
+
+ return new_ch_file.contents.?;
+ }
+
+ /// Add a file as a dependency of process being cached, after the initial hash has been
+ /// calculated. This is useful for processes that don't know the all the files that
+ /// are depended on ahead of time. For example, a source file that can import other files
+ /// will need to be recompiled if the imported file is changed.
+ pub fn addFilePost(self: *Manifest, file_path: []const u8) !void {
+ assert(self.manifest_file != null);
+
+ const resolved_path = try fs.path.resolve(self.cache.gpa, &[_][]const u8{file_path});
+ errdefer self.cache.gpa.free(resolved_path);
+
+ const new_ch_file = try self.files.addOne(self.cache.gpa);
+ new_ch_file.* = .{
+ .path = resolved_path,
+ .max_file_size = null,
+ .stat = undefined,
+ .bin_digest = undefined,
+ .contents = null,
+ };
+ errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1);
+
+ try self.populateFileHash(new_ch_file);
+ }
+
+ pub fn addDepFilePost(self: *Manifest, dir: fs.Dir, dep_file_basename: []const u8) !void {
+ assert(self.manifest_file != null);
+
+ const dep_file_contents = try dir.readFileAlloc(self.cache.gpa, dep_file_basename, manifest_file_size_max);
+ defer self.cache.gpa.free(dep_file_contents);
+
+ var error_buf = std.ArrayList(u8).init(self.cache.gpa);
+ defer error_buf.deinit();
+
+ var it: @import("DepTokenizer.zig") = .{ .bytes = dep_file_contents };
+
+ // Skip first token: target.
+ switch (it.next() orelse return) { // Empty dep file OK.
+ .target, .target_must_resolve, .prereq => {},
+ else => |err| {
+ try err.printError(error_buf.writer());
+ std.log.err("failed parsing {}: {}", .{ dep_file_basename, error_buf.items });
+ return error.InvalidDepFile;
+ },
+ }
+ // Process 0+ preqreqs.
+ // Clang is invoked in single-source mode so we never get more targets.
+ while (true) {
+ switch (it.next() orelse return) {
+ .target, .target_must_resolve => return,
+ .prereq => |bytes| try self.addFilePost(bytes),
+ else => |err| {
+ try err.printError(error_buf.writer());
+ std.log.err("failed parsing {}: {}", .{ dep_file_basename, error_buf.items });
+ return error.InvalidDepFile;
+ },
+ }
+ }
+ }
+
+ /// Returns a hex encoded hash of the inputs.
+ pub fn final(self: *Manifest) [hex_digest_len]u8 {
+ assert(self.manifest_file != null);
+
+ // We don't close the manifest file yet, because we want to
+ // keep it locked until the API user is done using it.
+ // We also don't write out the manifest yet, because until
+ // cache_release is called we still might be working on creating
+ // the artifacts to cache.
+
+ var bin_digest: [bin_digest_len]u8 = undefined;
+ self.hash.hasher.final(&bin_digest);
+
+ var out_digest: [hex_digest_len]u8 = undefined;
+ _ = std.fmt.bufPrint(&out_digest, "{x}", .{bin_digest}) catch unreachable;
+
+ return out_digest;
+ }
+
+ pub fn writeManifest(self: *Manifest) !void {
+ const manifest_file = self.manifest_file.?;
+ if (!self.manifest_dirty) return;
+
+ var contents = std.ArrayList(u8).init(self.cache.gpa);
+ defer contents.deinit();
+
+ const writer = contents.writer();
+ var encoded_digest: [hex_digest_len]u8 = undefined;
+
+ for (self.files.items) |file| {
+ _ = std.fmt.bufPrint(&encoded_digest, "{x}", .{file.bin_digest}) catch unreachable;
+ try writer.print("{d} {d} {d} {s} {s}\n", .{
+ file.stat.size,
+ file.stat.inode,
+ file.stat.mtime,
+ &encoded_digest,
+ file.path,
+ });
+ }
+
+ try manifest_file.setEndPos(contents.items.len);
+ try manifest_file.pwriteAll(contents.items, 0);
+ self.manifest_dirty = false;
+ }
+
+ /// Obtain only the data needed to maintain a lock on the manifest file.
+ /// The `Manifest` remains safe to deinit.
+ /// Don't forget to call `writeManifest` before this!
+ pub fn toOwnedLock(self: *Manifest) Lock {
+ const manifest_file = self.manifest_file.?;
+ self.manifest_file = null;
+ return Lock{ .manifest_file = manifest_file };
+ }
+
+ /// Releases the manifest file and frees any memory the Manifest was using.
+ /// `Manifest.hit` must be called first.
+ /// Don't forget to call `writeManifest` before this!
+ pub fn deinit(self: *Manifest) void {
+ if (self.manifest_file) |file| {
+ file.close();
+ }
+ for (self.files.items) |*file| {
+ file.deinit(self.cache.gpa);
+ }
+ self.files.deinit(self.cache.gpa);
+ }
+};
+
+fn hashFile(file: fs.File, bin_digest: []u8) !void {
+ var buf: [1024]u8 = undefined;
+
+ var hasher = hasher_init;
+ while (true) {
+ const bytes_read = try file.read(&buf);
+ if (bytes_read == 0) break;
+ hasher.update(buf[0..bytes_read]);
+ }
+
+ hasher.final(bin_digest);
+}
+
+/// If the wall clock time, rounded to the same precision as the
+/// mtime, is equal to the mtime, then we cannot rely on this mtime
+/// yet. We will instead save an mtime value that indicates the hash
+/// must be unconditionally computed.
+/// This function recognizes the precision of mtime by looking at trailing
+/// zero bits of the seconds and nanoseconds.
+fn isProblematicTimestamp(fs_clock: i128) bool {
+ const wall_clock = std.time.nanoTimestamp();
+
+ // We have to break the nanoseconds into seconds and remainder nanoseconds
+ // to detect precision of seconds, because looking at the zero bits in base
+ // 2 would not detect precision of the seconds value.
+ const fs_sec = @intCast(i64, @divFloor(fs_clock, std.time.ns_per_s));
+ const fs_nsec = @intCast(i64, @mod(fs_clock, std.time.ns_per_s));
+ var wall_sec = @intCast(i64, @divFloor(wall_clock, std.time.ns_per_s));
+ var wall_nsec = @intCast(i64, @mod(wall_clock, std.time.ns_per_s));
+
+ // First make all the least significant zero bits in the fs_clock, also zero bits in the wall clock.
+ if (fs_nsec == 0) {
+ wall_nsec = 0;
+ if (fs_sec == 0) {
+ wall_sec = 0;
+ } else {
+ wall_sec &= @as(i64, -1) << @intCast(u6, @ctz(i64, fs_sec));
+ }
+ } else {
+ wall_nsec &= @as(i64, -1) << @intCast(u6, @ctz(i64, fs_nsec));
+ }
+ return wall_nsec == fs_nsec and wall_sec == fs_sec;
+}
+
+test "cache file and then recall it" {
+ if (std.Target.current.os.tag == .wasi) {
+ // https://github.com/ziglang/zig/issues/5437
+ return error.SkipZigTest;
+ }
+ const cwd = fs.cwd();
+
+ const temp_file = "test.txt";
+ const temp_manifest_dir = "temp_manifest_dir";
+
+ const ts = std.time.nanoTimestamp();
+ try cwd.writeFile(temp_file, "Hello, world!\n");
+
+ while (isProblematicTimestamp(ts)) {
+ std.time.sleep(1);
+ }
+
+ var digest1: [hex_digest_len]u8 = undefined;
+ var digest2: [hex_digest_len]u8 = undefined;
+
+ {
+ var cache = Cache{
+ .gpa = testing.allocator,
+ .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
+ };
+ defer cache.manifest_dir.close();
+
+ {
+ var ch = cache.obtain();
+ defer ch.deinit();
+
+ ch.hash.add(true);
+ ch.hash.add(@as(u16, 1234));
+ ch.hash.addBytes("1234");
+ _ = try ch.addFile(temp_file, null);
+
+ // There should be nothing in the cache
+ testing.expectEqual(false, try ch.hit());
+
+ digest1 = ch.final();
+ try ch.writeManifest();
+ }
+ {
+ var ch = cache.obtain();
+ defer ch.deinit();
+
+ ch.hash.add(true);
+ ch.hash.add(@as(u16, 1234));
+ ch.hash.addBytes("1234");
+ _ = try ch.addFile(temp_file, null);
+
+ // Cache hit! We just "built" the same file
+ testing.expect(try ch.hit());
+ digest2 = ch.final();
+
+ try ch.writeManifest();
+ }
+
+ testing.expectEqual(digest1, digest2);
+ }
+
+ try cwd.deleteTree(temp_manifest_dir);
+ try cwd.deleteFile(temp_file);
+}
+
+test "give problematic timestamp" {
+ var fs_clock = std.time.nanoTimestamp();
+ // to make it problematic, we make it only accurate to the second
+ fs_clock = @divTrunc(fs_clock, std.time.ns_per_s);
+ fs_clock *= std.time.ns_per_s;
+ testing.expect(isProblematicTimestamp(fs_clock));
+}
+
+test "give nonproblematic timestamp" {
+ testing.expect(!isProblematicTimestamp(std.time.nanoTimestamp() - std.time.ns_per_s));
+}
+
+test "check that changing a file makes cache fail" {
+ if (std.Target.current.os.tag == .wasi) {
+ // https://github.com/ziglang/zig/issues/5437
+ return error.SkipZigTest;
+ }
+ const cwd = fs.cwd();
+
+ const temp_file = "cache_hash_change_file_test.txt";
+ const temp_manifest_dir = "cache_hash_change_file_manifest_dir";
+ const original_temp_file_contents = "Hello, world!\n";
+ const updated_temp_file_contents = "Hello, world; but updated!\n";
+
+ try cwd.deleteTree(temp_manifest_dir);
+ try cwd.deleteTree(temp_file);
+
+ const ts = std.time.nanoTimestamp();
+ try cwd.writeFile(temp_file, original_temp_file_contents);
+
+ while (isProblematicTimestamp(ts)) {
+ std.time.sleep(1);
+ }
+
+ var digest1: [hex_digest_len]u8 = undefined;
+ var digest2: [hex_digest_len]u8 = undefined;
+
+ {
+ var cache = Cache{
+ .gpa = testing.allocator,
+ .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
+ };
+ defer cache.manifest_dir.close();
+
+ {
+ var ch = cache.obtain();
+ defer ch.deinit();
+
+ ch.hash.addBytes("1234");
+ const temp_file_idx = try ch.addFile(temp_file, 100);
+
+ // There should be nothing in the cache
+ testing.expectEqual(false, try ch.hit());
+
+ testing.expect(mem.eql(u8, original_temp_file_contents, ch.files.items[temp_file_idx].contents.?));
+
+ digest1 = ch.final();
+
+ try ch.writeManifest();
+ }
+
+ try cwd.writeFile(temp_file, updated_temp_file_contents);
+
+ {
+ var ch = cache.obtain();
+ defer ch.deinit();
+
+ ch.hash.addBytes("1234");
+ const temp_file_idx = try ch.addFile(temp_file, 100);
+
+ // A file that we depend on has been updated, so the cache should not contain an entry for it
+ testing.expectEqual(false, try ch.hit());
+
+ // The cache system does not keep the contents of re-hashed input files.
+ testing.expect(ch.files.items[temp_file_idx].contents == null);
+
+ digest2 = ch.final();
+
+ try ch.writeManifest();
+ }
+
+ testing.expect(!mem.eql(u8, digest1[0..], digest2[0..]));
+ }
+
+ try cwd.deleteTree(temp_manifest_dir);
+ try cwd.deleteTree(temp_file);
+}
+
+test "no file inputs" {
+ if (std.Target.current.os.tag == .wasi) {
+ // https://github.com/ziglang/zig/issues/5437
+ return error.SkipZigTest;
+ }
+ const cwd = fs.cwd();
+ const temp_manifest_dir = "no_file_inputs_manifest_dir";
+ defer cwd.deleteTree(temp_manifest_dir) catch {};
+
+ var digest1: [hex_digest_len]u8 = undefined;
+ var digest2: [hex_digest_len]u8 = undefined;
+
+ var cache = Cache{
+ .gpa = testing.allocator,
+ .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
+ };
+ defer cache.manifest_dir.close();
+
+ {
+ var ch = cache.obtain();
+ defer ch.deinit();
+
+ ch.hash.addBytes("1234");
+
+ // There should be nothing in the cache
+ testing.expectEqual(false, try ch.hit());
+
+ digest1 = ch.final();
+
+ try ch.writeManifest();
+ }
+ {
+ var ch = cache.obtain();
+ defer ch.deinit();
+
+ ch.hash.addBytes("1234");
+
+ testing.expect(try ch.hit());
+ digest2 = ch.final();
+ try ch.writeManifest();
+ }
+
+ testing.expectEqual(digest1, digest2);
+}
+
+test "Manifest with files added after initial hash work" {
+ if (std.Target.current.os.tag == .wasi) {
+ // https://github.com/ziglang/zig/issues/5437
+ return error.SkipZigTest;
+ }
+ const cwd = fs.cwd();
+
+ const temp_file1 = "cache_hash_post_file_test1.txt";
+ const temp_file2 = "cache_hash_post_file_test2.txt";
+ const temp_manifest_dir = "cache_hash_post_file_manifest_dir";
+
+ const ts1 = std.time.nanoTimestamp();
+ try cwd.writeFile(temp_file1, "Hello, world!\n");
+ try cwd.writeFile(temp_file2, "Hello world the second!\n");
+
+ while (isProblematicTimestamp(ts1)) {
+ std.time.sleep(1);
+ }
+
+ var digest1: [hex_digest_len]u8 = undefined;
+ var digest2: [hex_digest_len]u8 = undefined;
+ var digest3: [hex_digest_len]u8 = undefined;
+
+ {
+ var cache = Cache{
+ .gpa = testing.allocator,
+ .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
+ };
+ defer cache.manifest_dir.close();
+
+ {
+ var ch = cache.obtain();
+ defer ch.deinit();
+
+ ch.hash.addBytes("1234");
+ _ = try ch.addFile(temp_file1, null);
+
+ // There should be nothing in the cache
+ testing.expectEqual(false, try ch.hit());
+
+ _ = try ch.addFilePost(temp_file2);
+
+ digest1 = ch.final();
+ try ch.writeManifest();
+ }
+ {
+ var ch = cache.obtain();
+ defer ch.deinit();
+
+ ch.hash.addBytes("1234");
+ _ = try ch.addFile(temp_file1, null);
+
+ testing.expect(try ch.hit());
+ digest2 = ch.final();
+
+ try ch.writeManifest();
+ }
+ testing.expect(mem.eql(u8, &digest1, &digest2));
+
+ // Modify the file added after initial hash
+ const ts2 = std.time.nanoTimestamp();
+ try cwd.writeFile(temp_file2, "Hello world the second, updated\n");
+
+ while (isProblematicTimestamp(ts2)) {
+ std.time.sleep(1);
+ }
+
+ {
+ var ch = cache.obtain();
+ defer ch.deinit();
+
+ ch.hash.addBytes("1234");
+ _ = try ch.addFile(temp_file1, null);
+
+ // A file that we depend on has been updated, so the cache should not contain an entry for it
+ testing.expectEqual(false, try ch.hit());
+
+ _ = try ch.addFilePost(temp_file2);
+
+ digest3 = ch.final();
+
+ try ch.writeManifest();
+ }
+
+ testing.expect(!mem.eql(u8, &digest1, &digest3));
+ }
+
+ try cwd.deleteTree(temp_manifest_dir);
+ try cwd.deleteFile(temp_file1);
+ try cwd.deleteFile(temp_file2);
+}
diff --git a/src/Compilation.zig b/src/Compilation.zig
new file mode 100644
index 0000000000..623635a6b0
--- /dev/null
+++ b/src/Compilation.zig
@@ -0,0 +1,2882 @@
+const Compilation = @This();
+
+const std = @import("std");
+const mem = std.mem;
+const Allocator = std.mem.Allocator;
+const assert = std.debug.assert;
+const log = std.log.scoped(.compilation);
+const Target = std.Target;
+
+const Value = @import("value.zig").Value;
+const target_util = @import("target.zig");
+const Package = @import("Package.zig");
+const link = @import("link.zig");
+const trace = @import("tracy.zig").trace;
+const liveness = @import("liveness.zig");
+const build_options = @import("build_options");
+const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
+const glibc = @import("glibc.zig");
+const musl = @import("musl.zig");
+const mingw = @import("mingw.zig");
+const libunwind = @import("libunwind.zig");
+const libcxx = @import("libcxx.zig");
+const fatal = @import("main.zig").fatal;
+const Module = @import("Module.zig");
+const Cache = @import("Cache.zig");
+const stage1 = @import("stage1.zig");
+const translate_c = @import("translate_c.zig");
+
+/// General-purpose allocator. Used for both temporary and long-term storage.
+gpa: *Allocator,
+/// Arena-allocated memory used during initialization. Should be untouched until deinit.
+arena_state: std.heap.ArenaAllocator.State,
+bin_file: *link.File,
+c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{},
+stage1_lock: ?Cache.Lock = null,
+stage1_cache_manifest: *Cache.Manifest = undefined,
+
+link_error_flags: link.File.ErrorFlags = .{},
+
+work_queue: std.fifo.LinearFifo(Job, .Dynamic),
+
+/// The ErrorMsg memory is owned by the `CObject`, using Compilation's general purpose allocator.
+failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *ErrorMsg) = .{},
+
+keep_source_files_loaded: bool,
+use_clang: bool,
+sanitize_c: bool,
+/// When this is `true` it means invoking clang as a sub-process is expected to inherit
+/// stdin, stdout, stderr, and if it returns non success, to forward the exit code.
+/// Otherwise we attempt to parse the error messages and expose them via the Compilation API.
+/// This is `true` for `zig cc`, `zig c++`, and `zig translate-c`.
+clang_passthrough_mode: bool,
+clang_preprocessor_mode: ClangPreprocessorMode,
+/// Whether to print clang argvs to stdout.
+verbose_cc: bool,
+verbose_tokenize: bool,
+verbose_ast: bool,
+verbose_ir: bool,
+verbose_llvm_ir: bool,
+verbose_cimport: bool,
+verbose_llvm_cpu_features: bool,
+disable_c_depfile: bool,
+time_report: bool,
+stack_report: bool,
+
+c_source_files: []const CSourceFile,
+clang_argv: []const []const u8,
+cache_parent: *Cache,
+/// Path to own executable for invoking `zig clang`.
+self_exe_path: ?[]const u8,
+zig_lib_directory: Directory,
+local_cache_directory: Directory,
+global_cache_directory: Directory,
+libc_include_dir_list: []const []const u8,
+rand: *std.rand.Random,
+
+/// Populated when we build the libc++ static library. A Job to build this is placed in the queue
+/// and resolved before calling linker.flush().
+libcxx_static_lib: ?CRTFile = null,
+/// Populated when we build the libc++abi static library. A Job to build this is placed in the queue
+/// and resolved before calling linker.flush().
+libcxxabi_static_lib: ?CRTFile = null,
+/// Populated when we build the libunwind static library. A Job to build this is placed in the queue
+/// and resolved before calling linker.flush().
+libunwind_static_lib: ?CRTFile = null,
+/// Populated when we build the libc static library. A Job to build this is placed in the queue
+/// and resolved before calling linker.flush().
+libc_static_lib: ?CRTFile = null,
+/// Populated when we build the libcompiler_rt static library. A Job to build this is placed in the queue
+/// and resolved before calling linker.flush().
+compiler_rt_static_lib: ?CRTFile = null,
+
+glibc_so_files: ?glibc.BuiltSharedObjects = null,
+
+/// For example `Scrt1.o` and `libc_nonshared.a`. These are populated after building libc from source,
+/// The set of needed CRT (C runtime) files differs depending on the target and compilation settings.
+/// The key is the basename, and the value is the absolute path to the completed build artifact.
+crt_files: std.StringHashMapUnmanaged(CRTFile) = .{},
+
+/// Keeping track of this possibly open resource so we can close it later.
+owned_link_dir: ?std.fs.Dir,
+
+/// This is for stage1 and should be deleted upon completion of self-hosting.
+/// Don't use this for anything other than stage1 compatibility.
+color: @import("main.zig").Color = .Auto,
+
+test_filter: ?[]const u8,
+test_name_prefix: ?[]const u8,
+test_evented_io: bool,
+
+emit_h: ?EmitLoc,
+emit_asm: ?EmitLoc,
+emit_llvm_ir: ?EmitLoc,
+emit_analysis: ?EmitLoc,
+emit_docs: ?EmitLoc,
+
+pub const InnerError = Module.InnerError;
+
+pub const CRTFile = struct {
+ lock: Cache.Lock,
+ full_object_path: []const u8,
+
+ fn deinit(self: *CRTFile, gpa: *Allocator) void {
+ self.lock.release();
+ gpa.free(self.full_object_path);
+ self.* = undefined;
+ }
+};
+
+/// For passing to a C compiler.
+pub const CSourceFile = struct {
+ src_path: []const u8,
+ extra_flags: []const []const u8 = &[0][]const u8{},
+};
+
+const Job = union(enum) {
+ /// Write the machine code for a Decl to the output file.
+ codegen_decl: *Module.Decl,
+ /// The Decl needs to be analyzed and possibly export itself.
+ /// It may have already be analyzed, or it may have been determined
+ /// to be outdated; in this case perform semantic analysis again.
+ analyze_decl: *Module.Decl,
+ /// The source file containing the Decl has been updated, and so the
+ /// Decl may need its line number information updated in the debug info.
+ update_line_number: *Module.Decl,
+ /// Invoke the Clang compiler to create an object file, which gets linked
+ /// with the Compilation.
+ c_object: *CObject,
+
+ /// one of the glibc static objects
+ glibc_crt_file: glibc.CRTFile,
+ /// all of the glibc shared objects
+ glibc_shared_objects,
+ /// one of the musl static objects
+ musl_crt_file: musl.CRTFile,
+ /// one of the mingw-w64 static objects
+ mingw_crt_file: mingw.CRTFile,
+ /// libunwind.a, usually needed when linking libc
+ libunwind: void,
+ libcxx: void,
+ libcxxabi: void,
+ /// needed when producing a dynamic library or executable
+ libcompiler_rt: void,
+ /// needed when not linking libc and using LLVM for code generation because it generates
+ /// calls to, for example, memcpy and memset.
+ zig_libc: void,
+
+ /// Generate builtin.zig source code and write it into the correct place.
+ generate_builtin_zig: void,
+ /// Use stage1 C++ code to compile zig code into an object file.
+ stage1_module: void,
+
+ /// The value is the index into `link.File.Options.system_libs`.
+ windows_import_lib: usize,
+};
+
+pub const CObject = struct {
+ /// Relative to cwd. Owned by arena.
+ src: CSourceFile,
+ status: union(enum) {
+ new,
+ success: struct {
+ /// The outputted result. Owned by gpa.
+ object_path: []u8,
+ /// This is a file system lock on the cache hash manifest representing this
+ /// object. It prevents other invocations of the Zig compiler from interfering
+ /// with this object until released.
+ lock: Cache.Lock,
+ },
+ /// There will be a corresponding ErrorMsg in Compilation.failed_c_objects.
+ failure,
+ },
+
+ /// Returns if there was failure.
+ pub fn clearStatus(self: *CObject, gpa: *Allocator) bool {
+ switch (self.status) {
+ .new => return false,
+ .failure => {
+ self.status = .new;
+ return true;
+ },
+ .success => |*success| {
+ gpa.free(success.object_path);
+ success.lock.release();
+ self.status = .new;
+ return false;
+ },
+ }
+ }
+
+ pub fn destroy(self: *CObject, gpa: *Allocator) void {
+ _ = self.clearStatus(gpa);
+ gpa.destroy(self);
+ }
+};
+
+pub const AllErrors = struct {
+ arena: std.heap.ArenaAllocator.State,
+ list: []const Message,
+
+ pub const Message = struct {
+ src_path: []const u8,
+ line: usize,
+ column: usize,
+ byte_offset: usize,
+ msg: []const u8,
+
+ pub fn renderToStdErr(self: Message) void {
+ std.debug.print("{}:{}:{}: error: {}\n", .{
+ self.src_path,
+ self.line + 1,
+ self.column + 1,
+ self.msg,
+ });
+ }
+ };
+
+ pub fn deinit(self: *AllErrors, gpa: *Allocator) void {
+ self.arena.promote(gpa).deinit();
+ }
+
+ fn add(
+ arena: *std.heap.ArenaAllocator,
+ errors: *std.ArrayList(Message),
+ sub_file_path: []const u8,
+ source: []const u8,
+ simple_err_msg: ErrorMsg,
+ ) !void {
+ const loc = std.zig.findLineColumn(source, simple_err_msg.byte_offset);
+ try errors.append(.{
+ .src_path = try arena.allocator.dupe(u8, sub_file_path),
+ .msg = try arena.allocator.dupe(u8, simple_err_msg.msg),
+ .byte_offset = simple_err_msg.byte_offset,
+ .line = loc.line,
+ .column = loc.column,
+ });
+ }
+};
+
+pub const Directory = struct {
+ /// This field is redundant for operations that can act on the open directory handle
+ /// directly, but it is needed when passing the directory to a child process.
+ /// `null` means cwd.
+ path: ?[]const u8,
+ handle: std.fs.Dir,
+
+ pub fn join(self: Directory, allocator: *Allocator, paths: []const []const u8) ![]u8 {
+ if (self.path) |p| {
+ // TODO clean way to do this with only 1 allocation
+ const part2 = try std.fs.path.join(allocator, paths);
+ defer allocator.free(part2);
+ return std.fs.path.join(allocator, &[_][]const u8{ p, part2 });
+ } else {
+ return std.fs.path.join(allocator, paths);
+ }
+ }
+};
+
+pub const EmitLoc = struct {
+ /// If this is `null` it means the file will be output to the cache directory.
+ /// When provided, both the open file handle and the path name must outlive the `Compilation`.
+ directory: ?Compilation.Directory,
+ /// This may not have sub-directories in it.
+ basename: []const u8,
+};
+
+pub const ClangPreprocessorMode = enum {
+ no,
+ /// This means we are doing `zig cc -E -o `.
+ yes,
+ /// This means we are doing `zig cc -E`.
+ stdout,
+};
+
+pub const InitOptions = struct {
+ zig_lib_directory: Directory,
+ local_cache_directory: Directory,
+ global_cache_directory: Directory,
+ target: Target,
+ root_name: []const u8,
+ root_pkg: ?*Package,
+ output_mode: std.builtin.OutputMode,
+ rand: *std.rand.Random,
+ dynamic_linker: ?[]const u8 = null,
+ /// `null` means to not emit a binary file.
+ emit_bin: ?EmitLoc,
+ /// `null` means to not emit a C header file.
+ emit_h: ?EmitLoc = null,
+ /// `null` means to not emit assembly.
+ emit_asm: ?EmitLoc = null,
+ /// `null` means to not emit LLVM IR.
+ emit_llvm_ir: ?EmitLoc = null,
+ /// `null` means to not emit semantic analysis JSON.
+ emit_analysis: ?EmitLoc = null,
+ /// `null` means to not emit docs.
+ emit_docs: ?EmitLoc = null,
+ link_mode: ?std.builtin.LinkMode = null,
+ dll_export_fns: ?bool = false,
+ /// Normally when using LLD to link, Zig uses a file named "lld.id" in the
+ /// same directory as the output binary which contains the hash of the link
+ /// operation, allowing Zig to skip linking when the hash would be unchanged.
+ /// In the case that the output binary is being emitted into a directory which
+ /// is externally modified - essentially anything other than zig-cache - then
+ /// this flag would be set to disable this machinery to avoid false positives.
+ disable_lld_caching: bool = false,
+ object_format: ?std.builtin.ObjectFormat = null,
+ optimize_mode: std.builtin.Mode = .Debug,
+ keep_source_files_loaded: bool = false,
+ clang_argv: []const []const u8 = &[0][]const u8{},
+ lld_argv: []const []const u8 = &[0][]const u8{},
+ lib_dirs: []const []const u8 = &[0][]const u8{},
+ rpath_list: []const []const u8 = &[0][]const u8{},
+ c_source_files: []const CSourceFile = &[0]CSourceFile{},
+ link_objects: []const []const u8 = &[0][]const u8{},
+ framework_dirs: []const []const u8 = &[0][]const u8{},
+ frameworks: []const []const u8 = &[0][]const u8{},
+ system_libs: []const []const u8 = &[0][]const u8{},
+ link_libc: bool = false,
+ link_libcpp: bool = false,
+ want_pic: ?bool = null,
+ want_sanitize_c: ?bool = null,
+ want_stack_check: ?bool = null,
+ want_valgrind: ?bool = null,
+ use_llvm: ?bool = null,
+ use_lld: ?bool = null,
+ use_clang: ?bool = null,
+ rdynamic: bool = false,
+ strip: bool = false,
+ single_threaded: bool = false,
+ function_sections: bool = false,
+ is_native_os: bool,
+ time_report: bool = false,
+ stack_report: bool = false,
+ link_eh_frame_hdr: bool = false,
+ linker_script: ?[]const u8 = null,
+ version_script: ?[]const u8 = null,
+ override_soname: ?[]const u8 = null,
+ linker_gc_sections: ?bool = null,
+ linker_allow_shlib_undefined: ?bool = null,
+ linker_bind_global_refs_locally: ?bool = null,
+ each_lib_rpath: ?bool = null,
+ disable_c_depfile: bool = false,
+ linker_z_nodelete: bool = false,
+ linker_z_defs: bool = false,
+ clang_passthrough_mode: bool = false,
+ verbose_cc: bool = false,
+ verbose_link: bool = false,
+ verbose_tokenize: bool = false,
+ verbose_ast: bool = false,
+ verbose_ir: bool = false,
+ verbose_llvm_ir: bool = false,
+ verbose_cimport: bool = false,
+ verbose_llvm_cpu_features: bool = false,
+ is_test: bool = false,
+ test_evented_io: bool = false,
+ is_compiler_rt_or_libc: bool = false,
+ parent_compilation_link_libc: bool = false,
+ stack_size_override: ?u64 = null,
+ self_exe_path: ?[]const u8 = null,
+ version: ?std.builtin.Version = null,
+ libc_installation: ?*const LibCInstallation = null,
+ machine_code_model: std.builtin.CodeModel = .default,
+ clang_preprocessor_mode: ClangPreprocessorMode = .no,
+ /// This is for stage1 and should be deleted upon completion of self-hosting.
+ color: @import("main.zig").Color = .Auto,
+ test_filter: ?[]const u8 = null,
+ test_name_prefix: ?[]const u8 = null,
+ subsystem: ?std.Target.SubSystem = null,
+};
+
+pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
+ const is_dyn_lib = switch (options.output_mode) {
+ .Obj, .Exe => false,
+ .Lib => (options.link_mode orelse .Static) == .Dynamic,
+ };
+ const is_exe_or_dyn_lib = switch (options.output_mode) {
+ .Obj => false,
+ .Lib => is_dyn_lib,
+ .Exe => true,
+ };
+ const comp: *Compilation = comp: {
+ // For allocations that have the same lifetime as Compilation. This arena is used only during this
+ // initialization and then is freed in deinit().
+ var arena_allocator = std.heap.ArenaAllocator.init(gpa);
+ errdefer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ // We put the `Compilation` itself in the arena. Freeing the arena will free the module.
+ // It's initialized later after we prepare the initialization options.
+ const comp = try arena.create(Compilation);
+ const root_name = try arena.dupe(u8, options.root_name);
+
+ const ofmt = options.object_format orelse options.target.getObjectFormat();
+
+ // Make a decision on whether to use LLVM or our own backend.
+ const use_llvm = if (options.use_llvm) |explicit| explicit else blk: {
+ // If we have no zig code to compile, no need for LLVM.
+ if (options.root_pkg == null)
+ break :blk false;
+
+ // If we are the stage1 compiler, we depend on the stage1 c++ llvm backend
+ // to compile zig code.
+ if (build_options.is_stage1)
+ break :blk true;
+
+ // We would want to prefer LLVM for release builds when it is available, however
+ // we don't have an LLVM backend yet :)
+ // We would also want to prefer LLVM for architectures that we don't have self-hosted support for too.
+ break :blk false;
+ };
+ if (!use_llvm and options.machine_code_model != .default) {
+ return error.MachineCodeModelNotSupported;
+ }
+
+ // Make a decision on whether to use LLD or our own linker.
+ const use_lld = if (options.use_lld) |explicit| explicit else blk: {
+ if (!build_options.have_llvm)
+ break :blk false;
+
+ if (ofmt == .c)
+ break :blk false;
+
+ // Our linker can't handle objects or most advanced options yet.
+ if (options.link_objects.len != 0 or
+ options.c_source_files.len != 0 or
+ options.frameworks.len != 0 or
+ options.system_libs.len != 0 or
+ options.link_libc or options.link_libcpp or
+ options.link_eh_frame_hdr or
+ options.output_mode == .Lib or
+ options.lld_argv.len != 0 or
+ options.linker_script != null or options.version_script != null)
+ {
+ break :blk true;
+ }
+
+ if (use_llvm) {
+ // If stage1 generates an object file, self-hosted linker is not
+ // yet sophisticated enough to handle that.
+ break :blk options.root_pkg != null;
+ }
+
+ break :blk false;
+ };
+
+ const link_libc = options.link_libc or target_util.osRequiresLibC(options.target);
+
+ const must_dynamic_link = dl: {
+ if (target_util.cannotDynamicLink(options.target))
+ break :dl false;
+ if (is_exe_or_dyn_lib and link_libc and
+ (options.target.isGnuLibC() or target_util.osRequiresLibC(options.target)))
+ {
+ break :dl true;
+ }
+ if (options.system_libs.len != 0)
+ break :dl true;
+
+ break :dl false;
+ };
+ const default_link_mode: std.builtin.LinkMode = if (must_dynamic_link) .Dynamic else .Static;
+ const link_mode: std.builtin.LinkMode = if (options.link_mode) |lm| blk: {
+ if (lm == .Static and must_dynamic_link) {
+ return error.UnableToStaticLink;
+ }
+ break :blk lm;
+ } else default_link_mode;
+
+ const dll_export_fns = if (options.dll_export_fns) |explicit| explicit else is_dyn_lib;
+
+ const libc_dirs = try detectLibCIncludeDirs(
+ arena,
+ options.zig_lib_directory.path.?,
+ options.target,
+ options.is_native_os,
+ link_libc,
+ options.libc_installation,
+ );
+
+ const must_pic: bool = b: {
+ if (target_util.requiresPIC(options.target, link_libc))
+ break :b true;
+ break :b link_mode == .Dynamic;
+ };
+ const pic = if (options.want_pic) |explicit| pic: {
+ if (!explicit and must_pic) {
+ return error.TargetRequiresPIC;
+ }
+ break :pic explicit;
+ } else must_pic;
+
+ // Make a decision on whether to use Clang for translate-c and compiling C files.
+ const use_clang = if (options.use_clang) |explicit| explicit else blk: {
+ if (build_options.have_llvm) {
+ // Can't use it if we don't have it!
+ break :blk false;
+ }
+ // It's not planned to do our own translate-c or C compilation.
+ break :blk true;
+ };
+
+ const is_safe_mode = switch (options.optimize_mode) {
+ .Debug, .ReleaseSafe => true,
+ .ReleaseFast, .ReleaseSmall => false,
+ };
+
+ const sanitize_c = options.want_sanitize_c orelse is_safe_mode;
+
+ const stack_check: bool = b: {
+ if (!target_util.supportsStackProbing(options.target))
+ break :b false;
+ break :b options.want_stack_check orelse is_safe_mode;
+ };
+
+ const valgrind: bool = b: {
+ if (!target_util.hasValgrindSupport(options.target))
+ break :b false;
+ break :b options.want_valgrind orelse (options.optimize_mode == .Debug);
+ };
+
+ const single_threaded = options.single_threaded or target_util.isSingleThreaded(options.target);
+
+ const llvm_cpu_features: ?[*:0]const u8 = if (build_options.have_llvm and use_llvm) blk: {
+ var buf = std.ArrayList(u8).init(arena);
+ for (options.target.cpu.arch.allFeaturesList()) |feature, index_usize| {
+ const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize);
+ const is_enabled = options.target.cpu.features.isEnabled(index);
+
+ if (feature.llvm_name) |llvm_name| {
+ const plus_or_minus = "-+"[@boolToInt(is_enabled)];
+ try buf.ensureCapacity(buf.items.len + 2 + llvm_name.len);
+ buf.appendAssumeCapacity(plus_or_minus);
+ buf.appendSliceAssumeCapacity(llvm_name);
+ buf.appendSliceAssumeCapacity(",");
+ }
+ }
+ assert(mem.endsWith(u8, buf.items, ","));
+ buf.items[buf.items.len - 1] = 0;
+ buf.shrink(buf.items.len);
+ break :blk buf.items[0 .. buf.items.len - 1 :0].ptr;
+ } else null;
+
+ const strip = options.strip or !target_util.hasDebugInfo(options.target);
+
+ // We put everything into the cache hash that *cannot be modified during an incremental update*.
+ // For example, one cannot change the target between updates, but one can change source files,
+ // so the target goes into the cache hash, but source files do not. This is so that we can
+ // find the same binary and incrementally update it even if there are modified source files.
+ // We do this even if outputting to the current directory because we need somewhere to store
+ // incremental compilation metadata.
+ const cache = try arena.create(Cache);
+ cache.* = .{
+ .gpa = gpa,
+ .manifest_dir = try options.local_cache_directory.handle.makeOpenPath("h", .{}),
+ };
+ errdefer cache.manifest_dir.close();
+
+ // This is shared hasher state common to zig source and all C source files.
+ cache.hash.addBytes(build_options.version);
+ cache.hash.addBytes(options.zig_lib_directory.path orelse ".");
+ cache.hash.add(options.optimize_mode);
+ cache.hash.add(options.target.cpu.arch);
+ cache.hash.addBytes(options.target.cpu.model.name);
+ cache.hash.add(options.target.cpu.features.ints);
+ cache.hash.add(options.target.os.tag);
+ cache.hash.add(options.is_native_os);
+ cache.hash.add(options.target.abi);
+ cache.hash.add(ofmt);
+ cache.hash.add(pic);
+ cache.hash.add(stack_check);
+ cache.hash.add(link_mode);
+ cache.hash.add(options.function_sections);
+ cache.hash.add(strip);
+ cache.hash.add(link_libc);
+ cache.hash.add(options.link_libcpp);
+ cache.hash.add(options.output_mode);
+ cache.hash.add(options.machine_code_model);
+ cache.hash.add(options.emit_bin != null);
+ // TODO audit this and make sure everything is in it
+
+ const module: ?*Module = if (options.root_pkg) |root_pkg| blk: {
+ // Options that are specific to zig source files, that cannot be
+ // modified between incremental updates.
+ var hash = cache.hash;
+
+ // Here we put the root source file path name, but *not* with addFile. We want the
+ // hash to be the same regardless of the contents of the source file, because
+ // incremental compilation will handle it, but we do want to namespace different
+ // source file names because they are likely different compilations and therefore this
+ // would be likely to cause cache hits.
+ hash.addBytes(root_pkg.root_src_path);
+ hash.addOptionalBytes(root_pkg.root_src_directory.path);
+ hash.add(valgrind);
+ hash.add(single_threaded);
+ hash.add(options.target.os.getVersionRange());
+ hash.add(dll_export_fns);
+ hash.add(options.is_test);
+ hash.add(options.is_compiler_rt_or_libc);
+ hash.add(options.parent_compilation_link_libc);
+
+ const digest = hash.final();
+ const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
+ var artifact_dir = try options.local_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{});
+ errdefer artifact_dir.close();
+ const zig_cache_artifact_directory: Directory = .{
+ .handle = artifact_dir,
+ .path = if (options.local_cache_directory.path) |p|
+ try std.fs.path.join(arena, &[_][]const u8{ p, artifact_sub_dir })
+ else
+ artifact_sub_dir,
+ };
+
+ // TODO when we implement serialization and deserialization of incremental compilation metadata,
+ // this is where we would load it. We have open a handle to the directory where
+ // the output either already is, or will be.
+ // However we currently do not have serialization of such metadata, so for now
+ // we set up an empty Module that does the entire compilation fresh.
+
+ const root_scope = rs: {
+ if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) {
+ const root_scope = try gpa.create(Module.Scope.File);
+ root_scope.* = .{
+ .sub_file_path = root_pkg.root_src_path,
+ .source = .{ .unloaded = {} },
+ .contents = .{ .not_available = {} },
+ .status = .never_loaded,
+ .root_container = .{
+ .file_scope = root_scope,
+ .decls = .{},
+ },
+ };
+ break :rs &root_scope.base;
+ } else if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) {
+ const root_scope = try gpa.create(Module.Scope.ZIRModule);
+ root_scope.* = .{
+ .sub_file_path = root_pkg.root_src_path,
+ .source = .{ .unloaded = {} },
+ .contents = .{ .not_available = {} },
+ .status = .never_loaded,
+ .decls = .{},
+ };
+ break :rs &root_scope.base;
+ } else {
+ unreachable;
+ }
+ };
+
+ const module = try arena.create(Module);
+ module.* = .{
+ .gpa = gpa,
+ .comp = comp,
+ .root_pkg = root_pkg,
+ .root_scope = root_scope,
+ .zig_cache_artifact_directory = zig_cache_artifact_directory,
+ };
+ break :blk module;
+ } else null;
+ errdefer if (module) |zm| zm.deinit();
+
+ const error_return_tracing = !strip and switch (options.optimize_mode) {
+ .Debug, .ReleaseSafe => true,
+ .ReleaseFast, .ReleaseSmall => false,
+ };
+
+ // For resource management purposes.
+ var owned_link_dir: ?std.fs.Dir = null;
+ errdefer if (owned_link_dir) |*dir| dir.close();
+
+ const bin_file_emit: ?link.Emit = blk: {
+ const emit_bin = options.emit_bin orelse break :blk null;
+ if (emit_bin.directory) |directory| {
+ break :blk link.Emit{
+ .directory = directory,
+ .sub_path = emit_bin.basename,
+ };
+ }
+ if (module) |zm| {
+ break :blk link.Emit{
+ .directory = zm.zig_cache_artifact_directory,
+ .sub_path = emit_bin.basename,
+ };
+ }
+ // We could use the cache hash as is no problem, however, we increase
+ // the likelihood of cache hits by adding the first C source file
+ // path name (not contents) to the hash. This way if the user is compiling
+ // foo.c and bar.c as separate compilations, they get different cache
+ // directories.
+ var hash = cache.hash;
+ if (options.c_source_files.len >= 1) {
+ hash.addBytes(options.c_source_files[0].src_path);
+ } else if (options.link_objects.len >= 1) {
+ hash.addBytes(options.link_objects[0]);
+ }
+
+ const digest = hash.final();
+ const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
+ var artifact_dir = try options.local_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{});
+ owned_link_dir = artifact_dir;
+ const link_artifact_directory: Directory = .{
+ .handle = artifact_dir,
+ .path = try options.local_cache_directory.join(arena, &[_][]const u8{artifact_sub_dir}),
+ };
+ break :blk link.Emit{
+ .directory = link_artifact_directory,
+ .sub_path = emit_bin.basename,
+ };
+ };
+
+ if (!use_llvm and options.emit_h != null) {
+ fatal("TODO implement support for -femit-h in the self-hosted backend", .{});
+ }
+
+ var system_libs: std.StringArrayHashMapUnmanaged(void) = .{};
+ errdefer system_libs.deinit(gpa);
+ try system_libs.ensureCapacity(gpa, options.system_libs.len);
+ for (options.system_libs) |lib_name| {
+ system_libs.putAssumeCapacity(lib_name, {});
+ }
+
+ const bin_file = try link.File.openPath(gpa, .{
+ .emit = bin_file_emit,
+ .root_name = root_name,
+ .module = module,
+ .target = options.target,
+ .dynamic_linker = options.dynamic_linker,
+ .output_mode = options.output_mode,
+ .link_mode = link_mode,
+ .object_format = ofmt,
+ .optimize_mode = options.optimize_mode,
+ .use_lld = use_lld,
+ .use_llvm = use_llvm,
+ .link_libc = link_libc,
+ .link_libcpp = options.link_libcpp,
+ .objects = options.link_objects,
+ .frameworks = options.frameworks,
+ .framework_dirs = options.framework_dirs,
+ .system_libs = system_libs,
+ .lib_dirs = options.lib_dirs,
+ .rpath_list = options.rpath_list,
+ .strip = strip,
+ .is_native_os = options.is_native_os,
+ .function_sections = options.function_sections,
+ .allow_shlib_undefined = options.linker_allow_shlib_undefined,
+ .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false,
+ .z_nodelete = options.linker_z_nodelete,
+ .z_defs = options.linker_z_defs,
+ .stack_size_override = options.stack_size_override,
+ .linker_script = options.linker_script,
+ .version_script = options.version_script,
+ .gc_sections = options.linker_gc_sections,
+ .eh_frame_hdr = options.link_eh_frame_hdr,
+ .rdynamic = options.rdynamic,
+ .extra_lld_args = options.lld_argv,
+ .override_soname = options.override_soname,
+ .version = options.version,
+ .libc_installation = libc_dirs.libc_installation,
+ .pic = pic,
+ .valgrind = valgrind,
+ .stack_check = stack_check,
+ .single_threaded = single_threaded,
+ .verbose_link = options.verbose_link,
+ .machine_code_model = options.machine_code_model,
+ .dll_export_fns = dll_export_fns,
+ .error_return_tracing = error_return_tracing,
+ .llvm_cpu_features = llvm_cpu_features,
+ .is_compiler_rt_or_libc = options.is_compiler_rt_or_libc,
+ .parent_compilation_link_libc = options.parent_compilation_link_libc,
+ .each_lib_rpath = options.each_lib_rpath orelse false,
+ .disable_lld_caching = options.disable_lld_caching,
+ .subsystem = options.subsystem,
+ .is_test = options.is_test,
+ });
+ errdefer bin_file.destroy();
+ comp.* = .{
+ .gpa = gpa,
+ .arena_state = arena_allocator.state,
+ .zig_lib_directory = options.zig_lib_directory,
+ .local_cache_directory = options.local_cache_directory,
+ .global_cache_directory = options.global_cache_directory,
+ .bin_file = bin_file,
+ .emit_h = options.emit_h,
+ .emit_asm = options.emit_asm,
+ .emit_llvm_ir = options.emit_llvm_ir,
+ .emit_analysis = options.emit_analysis,
+ .emit_docs = options.emit_docs,
+ .work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa),
+ .keep_source_files_loaded = options.keep_source_files_loaded,
+ .use_clang = use_clang,
+ .clang_argv = options.clang_argv,
+ .c_source_files = options.c_source_files,
+ .cache_parent = cache,
+ .self_exe_path = options.self_exe_path,
+ .libc_include_dir_list = libc_dirs.libc_include_dir_list,
+ .sanitize_c = sanitize_c,
+ .rand = options.rand,
+ .clang_passthrough_mode = options.clang_passthrough_mode,
+ .clang_preprocessor_mode = options.clang_preprocessor_mode,
+ .verbose_cc = options.verbose_cc,
+ .verbose_tokenize = options.verbose_tokenize,
+ .verbose_ast = options.verbose_ast,
+ .verbose_ir = options.verbose_ir,
+ .verbose_llvm_ir = options.verbose_llvm_ir,
+ .verbose_cimport = options.verbose_cimport,
+ .verbose_llvm_cpu_features = options.verbose_llvm_cpu_features,
+ .disable_c_depfile = options.disable_c_depfile,
+ .owned_link_dir = owned_link_dir,
+ .color = options.color,
+ .time_report = options.time_report,
+ .stack_report = options.stack_report,
+ .test_filter = options.test_filter,
+ .test_name_prefix = options.test_name_prefix,
+ .test_evented_io = options.test_evented_io,
+ };
+ break :comp comp;
+ };
+ errdefer comp.destroy();
+
+ if (comp.bin_file.options.module) |mod| {
+ try comp.work_queue.writeItem(.{ .generate_builtin_zig = {} });
+ }
+
+ // Add a `CObject` for each `c_source_files`.
+ try comp.c_object_table.ensureCapacity(gpa, options.c_source_files.len);
+ for (options.c_source_files) |c_source_file| {
+ const c_object = try gpa.create(CObject);
+ errdefer gpa.destroy(c_object);
+
+ c_object.* = .{
+ .status = .{ .new = {} },
+ .src = c_source_file,
+ };
+ comp.c_object_table.putAssumeCapacityNoClobber(c_object, {});
+ }
+
+ if (comp.bin_file.options.emit != null and !comp.bin_file.options.is_compiler_rt_or_libc) {
+ // If we need to build glibc for the target, add work items for it.
+ // We go through the work queue so that building can be done in parallel.
+ if (comp.wantBuildGLibCFromSource()) {
+ try comp.addBuildingGLibCJobs();
+ }
+ if (comp.wantBuildMuslFromSource()) {
+ try comp.work_queue.ensureUnusedCapacity(5);
+ if (target_util.libc_needs_crti_crtn(comp.getTarget())) {
+ comp.work_queue.writeAssumeCapacity(&[_]Job{
+ .{ .musl_crt_file = .crti_o },
+ .{ .musl_crt_file = .crtn_o },
+ });
+ }
+ comp.work_queue.writeAssumeCapacity(&[_]Job{
+ .{ .musl_crt_file = .crt1_o },
+ .{ .musl_crt_file = .scrt1_o },
+ .{ .musl_crt_file = .libc_a },
+ });
+ }
+ if (comp.wantBuildMinGWFromSource()) {
+ const static_lib_jobs = [_]Job{
+ .{ .mingw_crt_file = .mingw32_lib },
+ .{ .mingw_crt_file = .msvcrt_os_lib },
+ .{ .mingw_crt_file = .mingwex_lib },
+ .{ .mingw_crt_file = .uuid_lib },
+ };
+ const crt_job: Job = .{ .mingw_crt_file = if (is_dyn_lib) .dllcrt2_o else .crt2_o };
+ try comp.work_queue.ensureUnusedCapacity(static_lib_jobs.len + 1);
+ comp.work_queue.writeAssumeCapacity(&static_lib_jobs);
+ comp.work_queue.writeItemAssumeCapacity(crt_job);
+
+ // When linking mingw-w64 there are some import libs we always need.
+ for (mingw.always_link_libs) |name| {
+ try comp.bin_file.options.system_libs.put(comp.gpa, name, .{});
+ }
+ }
+ // Generate Windows import libs.
+ if (comp.getTarget().os.tag == .windows) {
+ const count = comp.bin_file.options.system_libs.count();
+ try comp.work_queue.ensureUnusedCapacity(count);
+ var i: usize = 0;
+ while (i < count) : (i += 1) {
+ comp.work_queue.writeItemAssumeCapacity(.{ .windows_import_lib = i });
+ }
+ }
+ if (comp.wantBuildLibUnwindFromSource()) {
+ try comp.work_queue.writeItem(.{ .libunwind = {} });
+ }
+ if (build_options.have_llvm and comp.bin_file.options.output_mode != .Obj and
+ comp.bin_file.options.link_libcpp)
+ {
+ try comp.work_queue.writeItem(.libcxx);
+ try comp.work_queue.writeItem(.libcxxabi);
+ }
+ if (is_exe_or_dyn_lib and build_options.is_stage1) {
+ try comp.work_queue.writeItem(.{ .libcompiler_rt = {} });
+ if (!comp.bin_file.options.link_libc) {
+ try comp.work_queue.writeItem(.{ .zig_libc = {} });
+ }
+ }
+ }
+
+ if (build_options.is_stage1 and comp.bin_file.options.use_llvm) {
+ try comp.work_queue.writeItem(.{ .stage1_module = {} });
+ }
+
+ return comp;
+}
+
+fn releaseStage1Lock(comp: *Compilation) void {
+ if (comp.stage1_lock) |*lock| {
+ lock.release();
+ comp.stage1_lock = null;
+ }
+}
+
+pub fn destroy(self: *Compilation) void {
+ const optional_module = self.bin_file.options.module;
+ self.bin_file.destroy();
+ if (optional_module) |module| module.deinit();
+
+ self.releaseStage1Lock();
+
+ const gpa = self.gpa;
+ self.work_queue.deinit();
+
+ {
+ var it = self.crt_files.iterator();
+ while (it.next()) |entry| {
+ gpa.free(entry.key);
+ entry.value.deinit(gpa);
+ }
+ self.crt_files.deinit(gpa);
+ }
+
+ if (self.libunwind_static_lib) |*crt_file| {
+ crt_file.deinit(gpa);
+ }
+ if (self.libcxx_static_lib) |*crt_file| {
+ crt_file.deinit(gpa);
+ }
+ if (self.libcxxabi_static_lib) |*crt_file| {
+ crt_file.deinit(gpa);
+ }
+ if (self.compiler_rt_static_lib) |*crt_file| {
+ crt_file.deinit(gpa);
+ }
+ if (self.libc_static_lib) |*crt_file| {
+ crt_file.deinit(gpa);
+ }
+
+ for (self.c_object_table.items()) |entry| {
+ entry.key.destroy(gpa);
+ }
+ self.c_object_table.deinit(gpa);
+
+ for (self.failed_c_objects.items()) |entry| {
+ entry.value.destroy(gpa);
+ }
+ self.failed_c_objects.deinit(gpa);
+
+ self.cache_parent.manifest_dir.close();
+ if (self.owned_link_dir) |*dir| dir.close();
+
+ // This destroys `self`.
+ self.arena_state.promote(gpa).deinit();
+}
+
+pub fn getTarget(self: Compilation) Target {
+ return self.bin_file.options.target;
+}
+
+/// Detect changes to source files, perform semantic analysis, and update the output files.
+pub fn update(self: *Compilation) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ // For compiling C objects, we rely on the cache hash system to avoid duplicating work.
+ // Add a Job for each C object.
+ try self.work_queue.ensureUnusedCapacity(self.c_object_table.items().len);
+ for (self.c_object_table.items()) |entry| {
+ self.work_queue.writeItemAssumeCapacity(.{ .c_object = entry.key });
+ }
+
+ const use_stage1 = build_options.is_stage1 and self.bin_file.options.use_llvm;
+ if (!use_stage1) {
+ if (self.bin_file.options.module) |module| {
+ module.generation += 1;
+
+ // TODO Detect which source files changed.
+ // Until then we simulate a full cache miss. Source files could have been loaded for any reason;
+ // to force a refresh we unload now.
+ if (module.root_scope.cast(Module.Scope.File)) |zig_file| {
+ zig_file.unload(module.gpa);
+ module.analyzeContainer(&zig_file.root_container) catch |err| switch (err) {
+ error.AnalysisFail => {
+ assert(self.totalErrorCount() != 0);
+ },
+ else => |e| return e,
+ };
+ } else if (module.root_scope.cast(Module.Scope.ZIRModule)) |zir_module| {
+ zir_module.unload(module.gpa);
+ module.analyzeRootZIRModule(zir_module) catch |err| switch (err) {
+ error.AnalysisFail => {
+ assert(self.totalErrorCount() != 0);
+ },
+ else => |e| return e,
+ };
+ }
+ }
+ }
+
+ try self.performAllTheWork();
+
+ if (!use_stage1) {
+ if (self.bin_file.options.module) |module| {
+ // Process the deletion set.
+ while (module.deletion_set.popOrNull()) |decl| {
+ if (decl.dependants.items().len != 0) {
+ decl.deletion_flag = false;
+ continue;
+ }
+ try module.deleteDecl(decl);
+ }
+ }
+ }
+
+ if (self.totalErrorCount() != 0) {
+ // Skip flushing.
+ self.link_error_flags = .{};
+ return;
+ }
+
+ // This is needed before reading the error flags.
+ try self.bin_file.flush(self);
+
+ self.link_error_flags = self.bin_file.errorFlags();
+
+ // If there are any errors, we anticipate the source files being loaded
+ // to report error messages. Otherwise we unload all source files to save memory.
+ if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) {
+ if (self.bin_file.options.module) |module| {
+ module.root_scope.unload(self.gpa);
+ }
+ }
+}
+
+/// Having the file open for writing is problematic as far as executing the
+/// binary is concerned. This will remove the write flag, or close the file,
+/// or whatever is needed so that it can be executed.
+/// After this, one must call` makeFileWritable` before calling `update`.
+pub fn makeBinFileExecutable(self: *Compilation) !void {
+ return self.bin_file.makeExecutable();
+}
+
+pub fn makeBinFileWritable(self: *Compilation) !void {
+ return self.bin_file.makeWritable();
+}
+
+pub fn totalErrorCount(self: *Compilation) usize {
+ var total: usize = self.failed_c_objects.items().len;
+
+ if (self.bin_file.options.module) |module| {
+ total += module.failed_decls.items().len +
+ module.failed_exports.items().len +
+ module.failed_files.items().len;
+ }
+
+ // The "no entry point found" error only counts if there are no other errors.
+ if (total == 0) {
+ return @boolToInt(self.link_error_flags.no_entry_point_found);
+ }
+
+ return total;
+}
+
+pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
+ var arena = std.heap.ArenaAllocator.init(self.gpa);
+ errdefer arena.deinit();
+
+ var errors = std.ArrayList(AllErrors.Message).init(self.gpa);
+ defer errors.deinit();
+
+ for (self.failed_c_objects.items()) |entry| {
+ const c_object = entry.key;
+ const err_msg = entry.value;
+ try AllErrors.add(&arena, &errors, c_object.src.src_path, "", err_msg.*);
+ }
+ if (self.bin_file.options.module) |module| {
+ for (module.failed_files.items()) |entry| {
+ const scope = entry.key;
+ const err_msg = entry.value;
+ const source = try scope.getSource(module);
+ try AllErrors.add(&arena, &errors, scope.subFilePath(), source, err_msg.*);
+ }
+ for (module.failed_decls.items()) |entry| {
+ const decl = entry.key;
+ const err_msg = entry.value;
+ const source = try decl.scope.getSource(module);
+ try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
+ }
+ for (module.failed_exports.items()) |entry| {
+ const decl = entry.key.owner_decl;
+ const err_msg = entry.value;
+ const source = try decl.scope.getSource(module);
+ try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
+ }
+ }
+
+ if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) {
+ const global_err_src_path = blk: {
+ if (self.bin_file.options.module) |module| break :blk module.root_pkg.root_src_path;
+ if (self.c_source_files.len != 0) break :blk self.c_source_files[0].src_path;
+ if (self.bin_file.options.objects.len != 0) break :blk self.bin_file.options.objects[0];
+ break :blk "(no file)";
+ };
+ try errors.append(.{
+ .src_path = global_err_src_path,
+ .line = 0,
+ .column = 0,
+ .byte_offset = 0,
+ .msg = try std.fmt.allocPrint(&arena.allocator, "no entry point found", .{}),
+ });
+ }
+
+ assert(errors.items.len == self.totalErrorCount());
+
+ return AllErrors{
+ .list = try arena.allocator.dupe(AllErrors.Message, errors.items),
+ .arena = arena.state,
+ };
+}
+
+pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void {
+ while (self.work_queue.readItem()) |work_item| switch (work_item) {
+ .codegen_decl => |decl| switch (decl.analysis) {
+ .unreferenced => unreachable,
+ .in_progress => unreachable,
+ .outdated => unreachable,
+
+ .sema_failure,
+ .codegen_failure,
+ .dependency_failure,
+ .sema_failure_retryable,
+ => continue,
+
+ .complete, .codegen_failure_retryable => {
+ const module = self.bin_file.options.module.?;
+ if (decl.typed_value.most_recent.typed_value.val.cast(Value.Payload.Function)) |payload| {
+ switch (payload.func.analysis) {
+ .queued => module.analyzeFnBody(decl, payload.func) catch |err| switch (err) {
+ error.AnalysisFail => {
+ assert(payload.func.analysis != .in_progress);
+ continue;
+ },
+ error.OutOfMemory => return error.OutOfMemory,
+ },
+ .in_progress => unreachable,
+ .sema_failure, .dependency_failure => continue,
+ .success => {},
+ }
+ // Here we tack on additional allocations to the Decl's arena. The allocations are
+ // lifetime annotations in the ZIR.
+ var decl_arena = decl.typed_value.most_recent.arena.?.promote(module.gpa);
+ defer decl.typed_value.most_recent.arena.?.* = decl_arena.state;
+ log.debug("analyze liveness of {}\n", .{decl.name});
+ try liveness.analyze(module.gpa, &decl_arena.allocator, payload.func.analysis.success);
+ }
+
+ assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());
+
+ self.bin_file.updateDecl(module, decl) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.AnalysisFail => {
+ decl.analysis = .dependency_failure;
+ },
+ else => {
+ try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1);
+ module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
+ module.gpa,
+ decl.src(),
+ "unable to codegen: {}",
+ .{@errorName(err)},
+ ));
+ decl.analysis = .codegen_failure_retryable;
+ },
+ };
+ },
+ },
+ .analyze_decl => |decl| {
+ const module = self.bin_file.options.module.?;
+ module.ensureDeclAnalyzed(decl) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.AnalysisFail => continue,
+ };
+ },
+ .update_line_number => |decl| {
+ const module = self.bin_file.options.module.?;
+ self.bin_file.updateDeclLineNumber(module, decl) catch |err| {
+ try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1);
+ module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
+ module.gpa,
+ decl.src(),
+ "unable to update line number: {}",
+ .{@errorName(err)},
+ ));
+ decl.analysis = .codegen_failure_retryable;
+ };
+ },
+ .c_object => |c_object| {
+ self.updateCObject(c_object) catch |err| switch (err) {
+ error.AnalysisFail => continue,
+ else => {
+ try self.failed_c_objects.ensureCapacity(self.gpa, self.failed_c_objects.items().len + 1);
+ self.failed_c_objects.putAssumeCapacityNoClobber(c_object, try ErrorMsg.create(
+ self.gpa,
+ 0,
+ "unable to build C object: {}",
+ .{@errorName(err)},
+ ));
+ c_object.status = .{ .failure = {} };
+ },
+ };
+ },
+ .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: {}", .{@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: {}", .{@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: {}", .{@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: {}", .{@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: {}", .{@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: {}", .{@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: {}", .{@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: {}", .{@errorName(err)});
+ };
+ },
+ .libcompiler_rt => {
+ self.buildStaticLibFromZig("compiler_rt.zig", &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: {}", .{@errorName(err)});
+ };
+ },
+ .zig_libc => {
+ self.buildStaticLibFromZig("c.zig", &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: {}", .{@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: {}", .{@errorName(err)});
+ };
+ },
+ .stage1_module => {
+ if (!build_options.is_stage1)
+ unreachable;
+
+ self.updateStage1Module() catch |err| {
+ fatal("unable to build stage1 zig object: {}", .{@errorName(err)});
+ };
+ },
+ };
+}
+
+pub fn obtainCObjectCacheManifest(comp: *Compilation) Cache.Manifest {
+ var man = comp.cache_parent.obtain();
+
+ // Only things that need to be added on top of the base hash, and only things
+ // that apply both to @cImport and compiling C objects. No linking stuff here!
+ // Also nothing that applies only to compiling .zig code.
+ man.hash.add(comp.sanitize_c);
+ man.hash.addListOfBytes(comp.clang_argv);
+ man.hash.add(comp.bin_file.options.link_libcpp);
+ man.hash.addListOfBytes(comp.libc_include_dir_list);
+
+ return man;
+}
+
+test "cImport" {
+ _ = cImport;
+}
+
+const CImportResult = struct {
+ out_zig_path: []u8,
+ errors: []translate_c.ClangErrMsg,
+};
+
+/// Caller owns returned memory.
+/// This API is currently coupled pretty tightly to stage1's needs; it will need to be reworked
+/// a bit when we want to start using it from self-hosted.
+pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
+ if (!build_options.have_llvm)
+ return error.ZigCompilerNotBuiltWithLLVMExtensions;
+
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const cimport_zig_basename = "cimport.zig";
+
+ var man = comp.obtainCObjectCacheManifest();
+ defer man.deinit();
+
+ man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects
+ man.hash.addBytes(c_src);
+
+ // If the previous invocation resulted in clang errors, we will see a hit
+ // here with 0 files in the manifest, in which case it is actually a miss.
+ // We need to "unhit" in this case, to keep the digests matching.
+ const prev_hash_state = man.hash.peekBin();
+ const actual_hit = hit: {
+ const is_hit = try man.hit();
+ if (man.files.items.len == 0) {
+ man.unhit(prev_hash_state, 0);
+ break :hit false;
+ }
+ break :hit true;
+ };
+ const digest = if (!actual_hit) digest: {
+ var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ const tmp_digest = man.hash.peek();
+ const tmp_dir_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &tmp_digest });
+ var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath(tmp_dir_sub_path, .{});
+ defer zig_cache_tmp_dir.close();
+ const cimport_basename = "cimport.h";
+ const out_h_path = try comp.local_cache_directory.join(arena, &[_][]const u8{
+ tmp_dir_sub_path, cimport_basename,
+ });
+ const out_dep_path = try std.fmt.allocPrint(arena, "{}.d", .{out_h_path});
+
+ try zig_cache_tmp_dir.writeFile(cimport_basename, c_src);
+ if (comp.verbose_cimport) {
+ log.info("C import source: {}", .{out_h_path});
+ }
+
+ var argv = std.ArrayList([]const u8).init(comp.gpa);
+ defer argv.deinit();
+
+ try comp.addTranslateCCArgs(arena, &argv, .c, out_dep_path);
+
+ try argv.append(out_h_path);
+
+ if (comp.verbose_cc) {
+ dump_argv(argv.items);
+ }
+
+ // Convert to null terminated args.
+ const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, argv.items.len + 1);
+ new_argv_with_sentinel[argv.items.len] = null;
+ const new_argv = new_argv_with_sentinel[0..argv.items.len :null];
+ for (argv.items) |arg, i| {
+ new_argv[i] = try arena.dupeZ(u8, arg);
+ }
+
+ const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"});
+ const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path);
+ var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{};
+ const tree = translate_c.translate(
+ comp.gpa,
+ new_argv.ptr,
+ new_argv.ptr + new_argv.len,
+ &clang_errors,
+ c_headers_dir_path_z,
+ ) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.ASTUnitFailure => {
+ log.warn("clang API returned errors but due to a clang bug, it is not exposing the errors for zig to see. For more details: https://github.com/ziglang/zig/issues/4455", .{});
+ return error.ASTUnitFailure;
+ },
+ error.SemanticAnalyzeFail => {
+ return CImportResult{
+ .out_zig_path = "",
+ .errors = clang_errors,
+ };
+ },
+ };
+ defer tree.deinit();
+
+ if (comp.verbose_cimport) {
+ log.info("C import .d file: {}", .{out_dep_path});
+ }
+
+ const dep_basename = std.fs.path.basename(out_dep_path);
+ try man.addDepFilePost(zig_cache_tmp_dir, dep_basename);
+
+ const digest = man.final();
+ const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
+ var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{});
+ defer o_dir.close();
+
+ var out_zig_file = try o_dir.createFile(cimport_zig_basename, .{});
+ defer out_zig_file.close();
+
+ var bos = std.io.bufferedOutStream(out_zig_file.writer());
+ _ = try std.zig.render(comp.gpa, bos.writer(), tree);
+ try bos.flush();
+
+ man.writeManifest() catch |err| {
+ log.warn("failed to write cache manifest for C import: {}", .{@errorName(err)});
+ };
+
+ break :digest digest;
+ } else man.final();
+
+ const out_zig_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{
+ "o", &digest, cimport_zig_basename,
+ });
+ if (comp.verbose_cimport) {
+ log.info("C import output: {}\n", .{out_zig_path});
+ }
+ return CImportResult{
+ .out_zig_path = out_zig_path,
+ .errors = &[0]translate_c.ClangErrMsg{},
+ };
+}
+
+fn updateCObject(comp: *Compilation, c_object: *CObject) !void {
+ if (!build_options.have_llvm) {
+ return comp.failCObj(c_object, "clang not available: compiler built without LLVM extensions", .{});
+ }
+ const self_exe_path = comp.self_exe_path orelse
+ return comp.failCObj(c_object, "clang compilation disabled", .{});
+
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ if (c_object.clearStatus(comp.gpa)) {
+ // There was previous failure.
+ comp.failed_c_objects.removeAssertDiscard(c_object);
+ }
+
+ var man = comp.obtainCObjectCacheManifest();
+ defer man.deinit();
+
+ man.hash.add(comp.clang_preprocessor_mode);
+
+ _ = try man.addFile(c_object.src.src_path, null);
+ {
+ // Hash the extra flags, with special care to call addFile for file parameters.
+ // TODO this logic can likely be improved by utilizing clang_options_data.zig.
+ const file_args = [_][]const u8{"-include"};
+ var arg_i: usize = 0;
+ while (arg_i < c_object.src.extra_flags.len) : (arg_i += 1) {
+ const arg = c_object.src.extra_flags[arg_i];
+ man.hash.addBytes(arg);
+ for (file_args) |file_arg| {
+ if (mem.eql(u8, file_arg, arg) and arg_i + 1 < c_object.src.extra_flags.len) {
+ arg_i += 1;
+ _ = try man.addFile(c_object.src.extra_flags[arg_i], null);
+ }
+ }
+ }
+ }
+
+ var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ const c_source_basename = std.fs.path.basename(c_object.src.src_path);
+ // Special case when doing build-obj for just one C file. When there are more than one object
+ // file and building an object we need to link them together, but with just one it should go
+ // directly to the output file.
+ const direct_o = comp.c_source_files.len == 1 and comp.bin_file.options.module == null and
+ comp.bin_file.options.output_mode == .Obj and comp.bin_file.options.objects.len == 0;
+ const o_basename_noext = if (direct_o)
+ comp.bin_file.options.root_name
+ else
+ mem.split(c_source_basename, ".").next().?;
+ const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, comp.getTarget().oFileExt() });
+
+ const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: {
+ var argv = std.ArrayList([]const u8).init(comp.gpa);
+ defer argv.deinit();
+
+ // We can't know the digest until we do the C compiler invocation, so we need a temporary filename.
+ const out_obj_path = try comp.tmpFilePath(arena, o_basename);
+ var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{});
+ defer zig_cache_tmp_dir.close();
+
+ try argv.appendSlice(&[_][]const u8{ self_exe_path, "clang" });
+
+ const ext = classifyFileExt(c_object.src.src_path);
+ const out_dep_path: ?[]const u8 = if (comp.disable_c_depfile or !ext.clangSupportsDepFile())
+ null
+ else
+ try std.fmt.allocPrint(arena, "{}.d", .{out_obj_path});
+ try comp.addCCArgs(arena, &argv, ext, out_dep_path);
+
+ try argv.ensureCapacity(argv.items.len + 3);
+ switch (comp.clang_preprocessor_mode) {
+ .no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }),
+ .yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }),
+ .stdout => argv.appendAssumeCapacity("-E"),
+ }
+
+ try argv.append(c_object.src.src_path);
+ try argv.appendSlice(c_object.src.extra_flags);
+
+ if (comp.verbose_cc) {
+ dump_argv(argv.items);
+ }
+
+ const child = try std.ChildProcess.init(argv.items, arena);
+ defer child.deinit();
+
+ if (comp.clang_passthrough_mode) {
+ child.stdin_behavior = .Inherit;
+ child.stdout_behavior = .Inherit;
+ child.stderr_behavior = .Inherit;
+
+ const term = child.spawnAndWait() catch |err| {
+ return comp.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) });
+ };
+ switch (term) {
+ .Exited => |code| {
+ if (code != 0) {
+ // TODO https://github.com/ziglang/zig/issues/6342
+ std.process.exit(1);
+ }
+ if (comp.clang_preprocessor_mode == .stdout)
+ std.process.exit(0);
+ },
+ else => std.process.exit(1),
+ }
+ } else {
+ child.stdin_behavior = .Ignore;
+ child.stdout_behavior = .Pipe;
+ child.stderr_behavior = .Pipe;
+
+ try child.spawn();
+
+ const stdout_reader = child.stdout.?.reader();
+ const stderr_reader = child.stderr.?.reader();
+
+ // TODO https://github.com/ziglang/zig/issues/6343
+ const stdout = try stdout_reader.readAllAlloc(arena, std.math.maxInt(u32));
+ const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
+
+ const term = child.wait() catch |err| {
+ return comp.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) });
+ };
+
+ switch (term) {
+ .Exited => |code| {
+ if (code != 0) {
+ // TODO parse clang stderr and turn it into an error message
+ // and then call failCObjWithOwnedErrorMsg
+ log.err("clang failed with stderr: {}", .{stderr});
+ return comp.failCObj(c_object, "clang exited with code {}", .{code});
+ }
+ },
+ else => {
+ log.err("clang terminated with stderr: {}", .{stderr});
+ return comp.failCObj(c_object, "clang terminated unexpectedly", .{});
+ },
+ }
+ }
+
+ if (out_dep_path) |dep_file_path| {
+ const dep_basename = std.fs.path.basename(dep_file_path);
+ // Add the files depended on to the cache system.
+ try man.addDepFilePost(zig_cache_tmp_dir, dep_basename);
+ // Just to save disk space, we delete the file because it is never needed again.
+ zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| {
+ log.warn("failed to delete '{}': {}", .{ dep_file_path, @errorName(err) });
+ };
+ }
+
+ // Rename into place.
+ const digest = man.final();
+ const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
+ var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{});
+ defer o_dir.close();
+ const tmp_basename = std.fs.path.basename(out_obj_path);
+ try std.fs.rename(zig_cache_tmp_dir, tmp_basename, o_dir, o_basename);
+
+ man.writeManifest() catch |err| {
+ log.warn("failed to write cache manifest when compiling '{}': {}", .{ c_object.src.src_path, @errorName(err) });
+ };
+ break :blk digest;
+ };
+
+ c_object.status = .{
+ .success = .{
+ .object_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{
+ "o", &digest, o_basename,
+ }),
+ .lock = man.toOwnedLock(),
+ },
+ };
+}
+
+pub fn tmpFilePath(comp: *Compilation, arena: *Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
+ const s = std.fs.path.sep_str;
+ const rand_int = comp.rand.int(u64);
+ if (comp.local_cache_directory.path) |p| {
+ return std.fmt.allocPrint(arena, "{}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix });
+ } else {
+ return std.fmt.allocPrint(arena, "tmp" ++ s ++ "{x}-{s}", .{ rand_int, suffix });
+ }
+}
+
+pub fn addTranslateCCArgs(
+ comp: *Compilation,
+ arena: *Allocator,
+ argv: *std.ArrayList([]const u8),
+ ext: FileExt,
+ out_dep_path: ?[]const u8,
+) !void {
+ try argv.appendSlice(&[_][]const u8{ "-x", "c" });
+ try comp.addCCArgs(arena, argv, ext, out_dep_path);
+ // This gives us access to preprocessing entities, presumably at the cost of performance.
+ try argv.appendSlice(&[_][]const u8{ "-Xclang", "-detailed-preprocessing-record" });
+}
+
+/// Add common C compiler args between translate-c and C object compilation.
+pub fn addCCArgs(
+ comp: *Compilation,
+ arena: *Allocator,
+ argv: *std.ArrayList([]const u8),
+ ext: FileExt,
+ out_dep_path: ?[]const u8,
+) !void {
+ const target = comp.getTarget();
+
+ if (ext == .cpp) {
+ try argv.append("-nostdinc++");
+ }
+
+ // We don't ever put `-fcolor-diagnostics` or `-fno-color-diagnostics` because in passthrough mode
+ // we want Clang to infer it, and in normal mode we always want it off, which will be true since
+ // clang will detect stderr as a pipe rather than a terminal.
+ if (!comp.clang_passthrough_mode) {
+ // Make stderr more easily parseable.
+ try argv.append("-fno-caret-diagnostics");
+ }
+
+ if (comp.bin_file.options.function_sections) {
+ try argv.append("-ffunction-sections");
+ }
+
+ try argv.ensureCapacity(argv.items.len + comp.bin_file.options.framework_dirs.len * 2);
+ for (comp.bin_file.options.framework_dirs) |framework_dir| {
+ argv.appendAssumeCapacity("-iframework");
+ argv.appendAssumeCapacity(framework_dir);
+ }
+
+ if (comp.bin_file.options.link_libcpp) {
+ const libcxx_include_path = try std.fs.path.join(arena, &[_][]const u8{
+ comp.zig_lib_directory.path.?, "libcxx", "include",
+ });
+ const libcxxabi_include_path = try std.fs.path.join(arena, &[_][]const u8{
+ comp.zig_lib_directory.path.?, "libcxxabi", "include",
+ });
+
+ try argv.append("-isystem");
+ try argv.append(libcxx_include_path);
+
+ try argv.append("-isystem");
+ try argv.append(libcxxabi_include_path);
+
+ if (target.abi.isMusl()) {
+ try argv.append("-D_LIBCPP_HAS_MUSL_LIBC");
+ }
+ try argv.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS");
+ try argv.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS");
+ }
+
+ const llvm_triple = try @import("codegen/llvm.zig").targetTriple(arena, target);
+ try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple });
+
+ switch (ext) {
+ .c, .cpp, .h => {
+ try argv.appendSlice(&[_][]const u8{
+ "-nostdinc",
+ "-fno-spell-checking",
+ });
+
+ // According to Rich Felker libc headers are supposed to go before C language headers.
+ // However as noted by @dimenus, appending libc headers before c_headers breaks intrinsics
+ // and other compiler specific items.
+ const c_headers_dir = try std.fs.path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, "include" });
+ try argv.append("-isystem");
+ try argv.append(c_headers_dir);
+
+ for (comp.libc_include_dir_list) |include_dir| {
+ try argv.append("-isystem");
+ try argv.append(include_dir);
+ }
+
+ if (target.cpu.model.llvm_name) |llvm_name| {
+ try argv.appendSlice(&[_][]const u8{
+ "-Xclang", "-target-cpu", "-Xclang", llvm_name,
+ });
+ }
+
+ // It would be really nice if there was a more compact way to communicate this info to Clang.
+ const all_features_list = target.cpu.arch.allFeaturesList();
+ try argv.ensureCapacity(argv.items.len + all_features_list.len * 4);
+ for (all_features_list) |feature, index_usize| {
+ const index = @intCast(std.Target.Cpu.Feature.Set.Index, index_usize);
+ const is_enabled = target.cpu.features.isEnabled(index);
+
+ if (feature.llvm_name) |llvm_name| {
+ argv.appendSliceAssumeCapacity(&[_][]const u8{ "-Xclang", "-target-feature", "-Xclang" });
+ const plus_or_minus = "-+"[@boolToInt(is_enabled)];
+ const arg = try std.fmt.allocPrint(arena, "{c}{s}", .{ plus_or_minus, llvm_name });
+ argv.appendAssumeCapacity(arg);
+ }
+ }
+ const mcmodel = comp.bin_file.options.machine_code_model;
+ if (mcmodel != .default) {
+ try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={}", .{@tagName(mcmodel)}));
+ }
+
+ // windows.h has files such as pshpack1.h which do #pragma packing, triggering a clang warning.
+ // So for this target, we disable this warning.
+ if (target.os.tag == .windows and target.abi.isGnu()) {
+ try argv.append("-Wno-pragma-pack");
+ }
+
+ if (!comp.bin_file.options.strip) {
+ try argv.append("-g");
+ }
+
+ if (comp.haveFramePointer()) {
+ try argv.append("-fno-omit-frame-pointer");
+ } else {
+ try argv.append("-fomit-frame-pointer");
+ }
+
+ if (comp.sanitize_c) {
+ try argv.append("-fsanitize=undefined");
+ try argv.append("-fsanitize-trap=undefined");
+ }
+
+ switch (comp.bin_file.options.optimize_mode) {
+ .Debug => {
+ // windows c runtime requires -D_DEBUG if using debug libraries
+ try argv.append("-D_DEBUG");
+ try argv.append("-Og");
+
+ if (comp.bin_file.options.link_libc) {
+ try argv.append("-fstack-protector-strong");
+ try argv.append("--param");
+ try argv.append("ssp-buffer-size=4");
+ } else {
+ try argv.append("-fno-stack-protector");
+ }
+ },
+ .ReleaseSafe => {
+ // See the comment in the BuildModeFastRelease case for why we pass -O2 rather
+ // than -O3 here.
+ try argv.append("-O2");
+ if (comp.bin_file.options.link_libc) {
+ try argv.append("-D_FORTIFY_SOURCE=2");
+ try argv.append("-fstack-protector-strong");
+ try argv.append("--param");
+ try argv.append("ssp-buffer-size=4");
+ } else {
+ try argv.append("-fno-stack-protector");
+ }
+ },
+ .ReleaseFast => {
+ try argv.append("-DNDEBUG");
+ // Here we pass -O2 rather than -O3 because, although we do the equivalent of
+ // -O3 in Zig code, the justification for the difference here is that Zig
+ // has better detection and prevention of undefined behavior, so -O3 is safer for
+ // Zig code than it is for C code. Also, C programmers are used to their code
+ // running in -O2 and thus the -O3 path has been tested less.
+ try argv.append("-O2");
+ try argv.append("-fno-stack-protector");
+ },
+ .ReleaseSmall => {
+ try argv.append("-DNDEBUG");
+ try argv.append("-Os");
+ try argv.append("-fno-stack-protector");
+ },
+ }
+
+ if (target_util.supports_fpic(target) and comp.bin_file.options.pic) {
+ try argv.append("-fPIC");
+ }
+ },
+ .shared_library, .assembly, .ll, .bc, .unknown, .static_library, .object, .zig, .zir => {},
+ }
+ 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");
+ }
+
+ try argv.appendSlice(comp.clang_argv);
+}
+
+fn failCObj(comp: *Compilation, c_object: *CObject, comptime format: []const u8, args: anytype) InnerError {
+ @setCold(true);
+ const err_msg = try ErrorMsg.create(comp.gpa, 0, "unable to build C object: " ++ format, args);
+ return comp.failCObjWithOwnedErrorMsg(c_object, err_msg);
+}
+
+fn failCObjWithOwnedErrorMsg(comp: *Compilation, c_object: *CObject, err_msg: *ErrorMsg) InnerError {
+ {
+ errdefer err_msg.destroy(comp.gpa);
+ try comp.failed_c_objects.ensureCapacity(comp.gpa, comp.failed_c_objects.items().len + 1);
+ }
+ comp.failed_c_objects.putAssumeCapacityNoClobber(c_object, err_msg);
+ c_object.status = .failure;
+ return error.AnalysisFail;
+}
+
+pub const ErrorMsg = struct {
+ byte_offset: usize,
+ msg: []const u8,
+
+ pub fn create(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !*ErrorMsg {
+ const self = try gpa.create(ErrorMsg);
+ errdefer gpa.destroy(self);
+ self.* = try init(gpa, byte_offset, format, args);
+ return self;
+ }
+
+ /// Assumes the ErrorMsg struct and msg were both allocated with allocator.
+ pub fn destroy(self: *ErrorMsg, gpa: *Allocator) void {
+ self.deinit(gpa);
+ gpa.destroy(self);
+ }
+
+ pub fn init(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !ErrorMsg {
+ return ErrorMsg{
+ .byte_offset = byte_offset,
+ .msg = try std.fmt.allocPrint(gpa, format, args),
+ };
+ }
+
+ pub fn deinit(self: *ErrorMsg, gpa: *Allocator) void {
+ gpa.free(self.msg);
+ self.* = undefined;
+ }
+};
+
+pub const FileExt = enum {
+ c,
+ cpp,
+ h,
+ ll,
+ bc,
+ assembly,
+ shared_library,
+ object,
+ static_library,
+ zig,
+ zir,
+ unknown,
+
+ pub fn clangSupportsDepFile(ext: FileExt) bool {
+ return switch (ext) {
+ .c, .cpp, .h => true,
+
+ .ll,
+ .bc,
+ .assembly,
+ .shared_library,
+ .object,
+ .static_library,
+ .zig,
+ .zir,
+ .unknown,
+ => false,
+ };
+ }
+};
+
+pub fn hasObjectExt(filename: []const u8) bool {
+ return mem.endsWith(u8, filename, ".o") or mem.endsWith(u8, filename, ".obj");
+}
+
+pub fn hasStaticLibraryExt(filename: []const u8) bool {
+ return mem.endsWith(u8, filename, ".a") or mem.endsWith(u8, filename, ".lib");
+}
+
+pub fn hasCExt(filename: []const u8) bool {
+ return mem.endsWith(u8, filename, ".c");
+}
+
+pub fn hasCppExt(filename: []const u8) bool {
+ return mem.endsWith(u8, filename, ".C") or
+ mem.endsWith(u8, filename, ".cc") or
+ mem.endsWith(u8, filename, ".cpp") or
+ mem.endsWith(u8, filename, ".cxx");
+}
+
+pub fn hasAsmExt(filename: []const u8) bool {
+ return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S");
+}
+
+pub fn hasSharedLibraryExt(filename: []const u8) bool {
+ if (mem.endsWith(u8, filename, ".so") or
+ mem.endsWith(u8, filename, ".dll") or
+ mem.endsWith(u8, filename, ".dylib"))
+ {
+ return true;
+ }
+ // Look for .so.X, .so.X.Y, .so.X.Y.Z
+ var it = mem.split(filename, ".");
+ _ = it.next().?;
+ var so_txt = it.next() orelse return false;
+ while (!mem.eql(u8, so_txt, "so")) {
+ so_txt = it.next() orelse return false;
+ }
+ const n1 = it.next() orelse return false;
+ const n2 = it.next();
+ const n3 = it.next();
+
+ _ = std.fmt.parseInt(u32, n1, 10) catch return false;
+ if (n2) |x| _ = std.fmt.parseInt(u32, x, 10) catch return false;
+ if (n3) |x| _ = std.fmt.parseInt(u32, x, 10) catch return false;
+ if (it.next() != null) return false;
+
+ return true;
+}
+
+pub fn classifyFileExt(filename: []const u8) FileExt {
+ if (hasCExt(filename)) {
+ return .c;
+ } else if (hasCppExt(filename)) {
+ return .cpp;
+ } else if (mem.endsWith(u8, filename, ".ll")) {
+ return .ll;
+ } else if (mem.endsWith(u8, filename, ".bc")) {
+ return .bc;
+ } else if (hasAsmExt(filename)) {
+ return .assembly;
+ } else if (mem.endsWith(u8, filename, ".h")) {
+ return .h;
+ } else if (mem.endsWith(u8, filename, ".zig")) {
+ return .zig;
+ } else if (mem.endsWith(u8, filename, ".zir")) {
+ return .zir;
+ } else if (hasSharedLibraryExt(filename)) {
+ return .shared_library;
+ } else if (hasStaticLibraryExt(filename)) {
+ return .static_library;
+ } else if (hasObjectExt(filename)) {
+ return .object;
+ } else {
+ return .unknown;
+ }
+}
+
+test "classifyFileExt" {
+ std.testing.expectEqual(FileExt.cpp, classifyFileExt("foo.cc"));
+ std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.nim"));
+ std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so"));
+ std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1"));
+ std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1.2"));
+ std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1.2.3"));
+ std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.so.1.2.3~"));
+ std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig"));
+ std.testing.expectEqual(FileExt.zir, classifyFileExt("foo.zir"));
+}
+
+fn haveFramePointer(comp: *Compilation) bool {
+ // If you complicate this logic make sure you update the parent cache hash.
+ // Right now it's not in the cache hash because the value depends on optimize_mode
+ // and strip which are both already part of the hash.
+ return switch (comp.bin_file.options.optimize_mode) {
+ .Debug, .ReleaseSafe => !comp.bin_file.options.strip,
+ .ReleaseSmall, .ReleaseFast => false,
+ };
+}
+
+const LibCDirs = struct {
+ libc_include_dir_list: []const []const u8,
+ libc_installation: ?*const LibCInstallation,
+};
+
+fn detectLibCIncludeDirs(
+ arena: *Allocator,
+ zig_lib_dir: []const u8,
+ target: Target,
+ is_native_os: bool,
+ link_libc: bool,
+ libc_installation: ?*const LibCInstallation,
+) !LibCDirs {
+ if (!link_libc) {
+ return LibCDirs{
+ .libc_include_dir_list = &[0][]u8{},
+ .libc_installation = null,
+ };
+ }
+
+ if (libc_installation) |lci| {
+ return detectLibCFromLibCInstallation(arena, target, lci);
+ }
+
+ if (target_util.canBuildLibC(target)) {
+ const generic_name = target_util.libCGenericName(target);
+ // Some architectures are handled by the same set of headers.
+ const arch_name = if (target.abi.isMusl()) target_util.archMuslName(target.cpu.arch) else @tagName(target.cpu.arch);
+ const os_name = @tagName(target.os.tag);
+ // Musl's headers are ABI-agnostic and so they all have the "musl" ABI name.
+ const abi_name = if (target.abi.isMusl()) "musl" else @tagName(target.abi);
+ const s = std.fs.path.sep_str;
+ const arch_include_dir = try std.fmt.allocPrint(
+ arena,
+ "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-{}",
+ .{ zig_lib_dir, arch_name, os_name, abi_name },
+ );
+ const generic_include_dir = try std.fmt.allocPrint(
+ arena,
+ "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{}",
+ .{ zig_lib_dir, generic_name },
+ );
+ const arch_os_include_dir = try std.fmt.allocPrint(
+ arena,
+ "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-any",
+ .{ zig_lib_dir, @tagName(target.cpu.arch), os_name },
+ );
+ const generic_os_include_dir = try std.fmt.allocPrint(
+ arena,
+ "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{}-any",
+ .{ zig_lib_dir, os_name },
+ );
+
+ const list = try arena.alloc([]const u8, 4);
+ list[0] = arch_include_dir;
+ list[1] = generic_include_dir;
+ list[2] = arch_os_include_dir;
+ list[3] = generic_os_include_dir;
+ return LibCDirs{
+ .libc_include_dir_list = list,
+ .libc_installation = null,
+ };
+ }
+
+ if (is_native_os) {
+ const libc = try arena.create(LibCInstallation);
+ libc.* = try LibCInstallation.findNative(.{ .allocator = arena });
+ return detectLibCFromLibCInstallation(arena, target, libc);
+ }
+
+ return LibCDirs{
+ .libc_include_dir_list = &[0][]u8{},
+ .libc_installation = null,
+ };
+}
+
+fn detectLibCFromLibCInstallation(arena: *Allocator, target: Target, lci: *const LibCInstallation) !LibCDirs {
+ var list = std.ArrayList([]const u8).init(arena);
+ try list.ensureCapacity(4);
+
+ list.appendAssumeCapacity(lci.include_dir.?);
+
+ const is_redundant = mem.eql(u8, lci.sys_include_dir.?, lci.include_dir.?);
+ if (!is_redundant) list.appendAssumeCapacity(lci.sys_include_dir.?);
+
+ if (target.os.tag == .windows) {
+ if (std.fs.path.dirname(lci.include_dir.?)) |include_dir_parent| {
+ const um_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "um" });
+ list.appendAssumeCapacity(um_dir);
+
+ const shared_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "shared" });
+ list.appendAssumeCapacity(shared_dir);
+ }
+ }
+ return LibCDirs{
+ .libc_include_dir_list = list.items,
+ .libc_installation = lci,
+ };
+}
+
+pub fn get_libc_crt_file(comp: *Compilation, arena: *Allocator, basename: []const u8) ![]const u8 {
+ if (comp.wantBuildGLibCFromSource() or
+ comp.wantBuildMuslFromSource() or
+ comp.wantBuildMinGWFromSource())
+ {
+ return comp.crt_files.get(basename).?.full_object_path;
+ }
+ const lci = comp.bin_file.options.libc_installation orelse return error.LibCInstallationNotAvailable;
+ const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCRTDir;
+ const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename });
+ return full_path;
+}
+
+fn addBuildingGLibCJobs(comp: *Compilation) !void {
+ try comp.work_queue.write(&[_]Job{
+ .{ .glibc_crt_file = .crti_o },
+ .{ .glibc_crt_file = .crtn_o },
+ .{ .glibc_crt_file = .scrt1_o },
+ .{ .glibc_crt_file = .libc_nonshared_a },
+ .{ .glibc_shared_objects = {} },
+ });
+}
+
+fn wantBuildLibCFromSource(comp: Compilation) bool {
+ const is_exe_or_dyn_lib = switch (comp.bin_file.options.output_mode) {
+ .Obj => false,
+ .Lib => comp.bin_file.options.link_mode == .Dynamic,
+ .Exe => true,
+ };
+ return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and
+ comp.bin_file.options.libc_installation == null;
+}
+
+fn wantBuildGLibCFromSource(comp: Compilation) bool {
+ return comp.wantBuildLibCFromSource() and comp.getTarget().isGnuLibC();
+}
+
+fn wantBuildMuslFromSource(comp: Compilation) bool {
+ return comp.wantBuildLibCFromSource() and comp.getTarget().isMusl();
+}
+
+fn wantBuildMinGWFromSource(comp: Compilation) bool {
+ return comp.wantBuildLibCFromSource() and comp.getTarget().isMinGW();
+}
+
+fn wantBuildLibUnwindFromSource(comp: *Compilation) bool {
+ const is_exe_or_dyn_lib = switch (comp.bin_file.options.output_mode) {
+ .Obj => false,
+ .Lib => comp.bin_file.options.link_mode == .Dynamic,
+ .Exe => true,
+ };
+ return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and
+ comp.bin_file.options.libc_installation == null and
+ target_util.libcNeedsLibUnwind(comp.getTarget());
+}
+
+fn updateBuiltinZigFile(comp: *Compilation, mod: *Module) !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);
+}
+
+pub fn dump_argv(argv: []const []const u8) void {
+ for (argv[0 .. argv.len - 1]) |arg| {
+ std.debug.print("{} ", .{arg});
+ }
+ std.debug.print("{}\n", .{argv[argv.len - 1]});
+}
+
+pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ var buffer = std.ArrayList(u8).init(allocator);
+ defer buffer.deinit();
+
+ const target = comp.getTarget();
+ const generic_arch_name = target.cpu.arch.genericName();
+
+ @setEvalBranchQuota(4000);
+ try buffer.writer().print(
+ \\usingnamespace @import("std").builtin;
+ \\/// Deprecated
+ \\pub const arch = Target.current.cpu.arch;
+ \\/// Deprecated
+ \\pub const endian = Target.current.cpu.arch.endian();
+ \\pub const output_mode = OutputMode.{};
+ \\pub const link_mode = LinkMode.{};
+ \\pub const is_test = {};
+ \\pub const single_threaded = {};
+ \\pub const abi = Abi.{};
+ \\pub const cpu: Cpu = Cpu{{
+ \\ .arch = .{},
+ \\ .model = &Target.{}.cpu.{},
+ \\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{
+ \\
+ , .{
+ @tagName(comp.bin_file.options.output_mode),
+ @tagName(comp.bin_file.options.link_mode),
+ comp.bin_file.options.is_test,
+ comp.bin_file.options.single_threaded,
+ @tagName(target.abi),
+ @tagName(target.cpu.arch),
+ generic_arch_name,
+ target.cpu.model.name,
+ generic_arch_name,
+ generic_arch_name,
+ });
+
+ for (target.cpu.arch.allFeaturesList()) |feature, index_usize| {
+ const index = @intCast(std.Target.Cpu.Feature.Set.Index, index_usize);
+ const is_enabled = target.cpu.features.isEnabled(index);
+ if (is_enabled) {
+ // TODO some kind of "zig identifier escape" function rather than
+ // unconditionally using @"" syntax
+ try buffer.appendSlice(" .@\"");
+ try buffer.appendSlice(feature.name);
+ try buffer.appendSlice("\",\n");
+ }
+ }
+
+ try buffer.writer().print(
+ \\ }}),
+ \\}};
+ \\pub const os = Os{{
+ \\ .tag = .{},
+ \\ .version_range = .{{
+ ,
+ .{@tagName(target.os.tag)},
+ );
+
+ switch (target.os.getVersionRange()) {
+ .none => try buffer.appendSlice(" .none = {} }\n"),
+ .semver => |semver| try buffer.outStream().print(
+ \\ .semver = .{{
+ \\ .min = .{{
+ \\ .major = {},
+ \\ .minor = {},
+ \\ .patch = {},
+ \\ }},
+ \\ .max = .{{
+ \\ .major = {},
+ \\ .minor = {},
+ \\ .patch = {},
+ \\ }},
+ \\ }}}},
+ \\
+ , .{
+ semver.min.major,
+ semver.min.minor,
+ semver.min.patch,
+
+ semver.max.major,
+ semver.max.minor,
+ semver.max.patch,
+ }),
+ .linux => |linux| try buffer.outStream().print(
+ \\ .linux = .{{
+ \\ .range = .{{
+ \\ .min = .{{
+ \\ .major = {},
+ \\ .minor = {},
+ \\ .patch = {},
+ \\ }},
+ \\ .max = .{{
+ \\ .major = {},
+ \\ .minor = {},
+ \\ .patch = {},
+ \\ }},
+ \\ }},
+ \\ .glibc = .{{
+ \\ .major = {},
+ \\ .minor = {},
+ \\ .patch = {},
+ \\ }},
+ \\ }}}},
+ \\
+ , .{
+ linux.range.min.major,
+ linux.range.min.minor,
+ linux.range.min.patch,
+
+ linux.range.max.major,
+ linux.range.max.minor,
+ linux.range.max.patch,
+
+ linux.glibc.major,
+ linux.glibc.minor,
+ linux.glibc.patch,
+ }),
+ .windows => |windows| try buffer.outStream().print(
+ \\ .windows = .{{
+ \\ .min = {s},
+ \\ .max = {s},
+ \\ }}}},
+ \\
+ ,
+ .{ windows.min, windows.max },
+ ),
+ }
+ try buffer.appendSlice("};\n");
+
+ // This is so that compiler_rt and libc.zig libraries know whether they
+ // will eventually be linked with libc. They make different decisions
+ // about what to export depending on whether another libc will be linked
+ // in. For example, compiler_rt will not export the __chkstk symbol if it
+ // knows libc will provide it, and likewise c.zig will not export memcpy.
+ const link_libc = comp.bin_file.options.link_libc or
+ (comp.bin_file.options.is_compiler_rt_or_libc and comp.bin_file.options.parent_compilation_link_libc);
+
+ try buffer.writer().print(
+ \\pub const object_format = ObjectFormat.{};
+ \\pub const mode = Mode.{};
+ \\pub const link_libc = {};
+ \\pub const link_libcpp = {};
+ \\pub const have_error_return_tracing = {};
+ \\pub const valgrind_support = {};
+ \\pub const position_independent_code = {};
+ \\pub const strip_debug_info = {};
+ \\pub const code_model = CodeModel.{};
+ \\
+ , .{
+ @tagName(comp.bin_file.options.object_format),
+ @tagName(comp.bin_file.options.optimize_mode),
+ link_libc,
+ comp.bin_file.options.link_libcpp,
+ comp.bin_file.options.error_return_tracing,
+ comp.bin_file.options.valgrind,
+ comp.bin_file.options.pic,
+ comp.bin_file.options.strip,
+ @tagName(comp.bin_file.options.machine_code_model),
+ });
+
+ if (comp.bin_file.options.is_test) {
+ try buffer.appendSlice(
+ \\pub var test_functions: []TestFn = undefined; // overwritten later
+ \\
+ );
+ if (comp.test_evented_io) {
+ try buffer.appendSlice(
+ \\pub const test_io_mode = .evented;
+ \\
+ );
+ } else {
+ try buffer.appendSlice(
+ \\pub const test_io_mode = .blocking;
+ \\
+ );
+ }
+ }
+
+ return buffer.toOwnedSlice();
+}
+
+pub fn updateSubCompilation(sub_compilation: *Compilation) !void {
+ try sub_compilation.update();
+
+ // Look for compilation errors in this sub_compilation
+ var errors = try sub_compilation.getAllErrorsAlloc();
+ defer errors.deinit(sub_compilation.gpa);
+
+ if (errors.list.len != 0) {
+ for (errors.list) |full_err_msg| {
+ log.err("{}:{}:{}: {}\n", .{
+ full_err_msg.src_path,
+ full_err_msg.line + 1,
+ full_err_msg.column + 1,
+ full_err_msg.msg,
+ });
+ }
+ return error.BuildingLibCObjectFailed;
+ }
+}
+
+fn buildStaticLibFromZig(comp: *Compilation, src_basename: []const u8, out: *?CRTFile) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const special_sub = "std" ++ std.fs.path.sep_str ++ "special";
+ const special_path = try comp.zig_lib_directory.join(comp.gpa, &[_][]const u8{special_sub});
+ defer comp.gpa.free(special_path);
+
+ var special_dir = try comp.zig_lib_directory.handle.openDir(special_sub, .{});
+ defer special_dir.close();
+
+ var root_pkg: Package = .{
+ .root_src_directory = .{
+ .path = special_path,
+ .handle = special_dir,
+ },
+ .root_src_path = src_basename,
+ };
+ const root_name = mem.split(src_basename, ".").next().?;
+ const target = comp.getTarget();
+ const output_mode: std.builtin.OutputMode = if (target.cpu.arch.isWasm()) .Obj else .Lib;
+ const bin_basename = try std.zig.binNameAlloc(comp.gpa, .{
+ .root_name = root_name,
+ .target = target,
+ .output_mode = output_mode,
+ });
+ defer comp.gpa.free(bin_basename);
+
+ const emit_bin = Compilation.EmitLoc{
+ .directory = null, // Put it in the cache directory.
+ .basename = bin_basename,
+ };
+ const optimize_mode: std.builtin.Mode = blk: {
+ if (comp.bin_file.options.is_test)
+ break :blk comp.bin_file.options.optimize_mode;
+ switch (comp.bin_file.options.optimize_mode) {
+ .Debug, .ReleaseFast, .ReleaseSafe => break :blk .ReleaseFast,
+ .ReleaseSmall => break :blk .ReleaseSmall,
+ }
+ };
+ const sub_compilation = try Compilation.create(comp.gpa, .{
+ .global_cache_directory = comp.global_cache_directory,
+ .local_cache_directory = comp.global_cache_directory,
+ .zig_lib_directory = comp.zig_lib_directory,
+ .target = target,
+ .root_name = root_name,
+ .root_pkg = &root_pkg,
+ .output_mode = output_mode,
+ .rand = comp.rand,
+ .libc_installation = comp.bin_file.options.libc_installation,
+ .emit_bin = emit_bin,
+ .optimize_mode = optimize_mode,
+ .link_mode = .Static,
+ .function_sections = true,
+ .want_sanitize_c = false,
+ .want_stack_check = false,
+ .want_valgrind = false,
+ .want_pic = comp.bin_file.options.pic,
+ .emit_h = null,
+ .strip = comp.bin_file.options.strip,
+ .is_native_os = comp.bin_file.options.is_native_os,
+ .self_exe_path = comp.self_exe_path,
+ .verbose_cc = comp.verbose_cc,
+ .verbose_link = comp.bin_file.options.verbose_link,
+ .verbose_tokenize = comp.verbose_tokenize,
+ .verbose_ast = comp.verbose_ast,
+ .verbose_ir = comp.verbose_ir,
+ .verbose_llvm_ir = comp.verbose_llvm_ir,
+ .verbose_cimport = comp.verbose_cimport,
+ .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
+ .clang_passthrough_mode = comp.clang_passthrough_mode,
+ .is_compiler_rt_or_libc = true,
+ .parent_compilation_link_libc = comp.bin_file.options.link_libc,
+ });
+ defer sub_compilation.destroy();
+
+ try sub_compilation.updateSubCompilation();
+
+ assert(out.* == null);
+ out.* = Compilation.CRTFile{
+ .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{
+ sub_compilation.bin_file.options.emit.?.sub_path,
+ }),
+ .lock = sub_compilation.bin_file.toOwnedLock(),
+ };
+}
+
+fn updateStage1Module(comp: *Compilation) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ // Here we use the legacy stage1 C++ compiler to compile Zig code.
+ const mod = comp.bin_file.options.module.?;
+ const directory = mod.zig_cache_artifact_directory; // Just an alias to make it shorter to type.
+ const main_zig_file = try mod.root_pkg.root_src_directory.join(arena, &[_][]const u8{
+ mod.root_pkg.root_src_path,
+ });
+ const zig_lib_dir = comp.zig_lib_directory.path.?;
+ const builtin_zig_path = try directory.join(arena, &[_][]const u8{"builtin.zig"});
+ const target = comp.getTarget();
+ const id_symlink_basename = "stage1.id";
+ const libs_txt_basename = "libs.txt";
+
+ // We are about to obtain this lock, so here we give other processes a chance first.
+ comp.releaseStage1Lock();
+
+ // Unlike with the self-hosted Zig module, stage1 does not support incremental compilation,
+ // so we input all the zig source files into the cache hash system. We're going to keep
+ // the artifact directory the same, however, so we take the same strategy as linking
+ // does where we have a file which specifies the hash of the output directory so that we can
+ // skip the expensive compilation step if the hash matches.
+ var man = comp.cache_parent.obtain();
+ defer man.deinit();
+
+ _ = try man.addFile(main_zig_file, null);
+ man.hash.add(comp.bin_file.options.valgrind);
+ man.hash.add(comp.bin_file.options.single_threaded);
+ man.hash.add(target.os.getVersionRange());
+ man.hash.add(comp.bin_file.options.dll_export_fns);
+ man.hash.add(comp.bin_file.options.function_sections);
+ man.hash.add(comp.bin_file.options.is_test);
+ man.hash.add(comp.bin_file.options.emit != null);
+ man.hash.add(comp.emit_h != null);
+ man.hash.add(comp.emit_asm != null);
+ man.hash.add(comp.emit_llvm_ir != null);
+ man.hash.add(comp.emit_analysis != null);
+ man.hash.add(comp.emit_docs != null);
+
+ // Capture the state in case we come back from this branch where the hash doesn't match.
+ const prev_hash_state = man.hash.peekBin();
+ const input_file_count = man.files.items.len;
+
+ if (try man.hit()) {
+ const digest = man.final();
+
+ // We use an extra hex-encoded byte here to store some flags.
+ var prev_digest_buf: [digest.len + 2]u8 = undefined;
+ const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {
+ log.debug("stage1 {} new_digest={} readlink error: {}", .{ mod.root_pkg.root_src_path, digest, @errorName(err) });
+ // Handle this as a cache miss.
+ break :blk prev_digest_buf[0..0];
+ };
+ if (prev_digest.len >= digest.len + 2) hit: {
+ if (!mem.eql(u8, prev_digest[0..digest.len], &digest))
+ break :hit;
+
+ log.debug("stage1 {} digest={} match - skipping invocation", .{ mod.root_pkg.root_src_path, digest });
+ var flags_bytes: [1]u8 = undefined;
+ _ = std.fmt.hexToBytes(&flags_bytes, prev_digest[digest.len..]) catch {
+ log.warn("bad cache stage1 digest: '{s}'", .{prev_digest});
+ break :hit;
+ };
+
+ if (directory.handle.readFileAlloc(comp.gpa, libs_txt_basename, 10 * 1024 * 1024)) |libs_txt| {
+ var it = mem.tokenize(libs_txt, "\n");
+ while (it.next()) |lib_name| {
+ try comp.stage1AddLinkLib(lib_name);
+ }
+ } else |err| switch (err) {
+ error.FileNotFound => {}, // That's OK, it just means 0 libs.
+ else => {
+ log.warn("unable to read cached list of link libs: {s}", .{@errorName(err)});
+ break :hit;
+ },
+ }
+ comp.stage1_lock = man.toOwnedLock();
+ mod.stage1_flags = @bitCast(@TypeOf(mod.stage1_flags), flags_bytes[0]);
+ return;
+ }
+ log.debug("stage1 {} prev_digest={} new_digest={}", .{ mod.root_pkg.root_src_path, prev_digest, digest });
+ man.unhit(prev_hash_state, input_file_count);
+ }
+
+ // We are about to change the output file to be different, so we invalidate the build hash now.
+ directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
+ error.FileNotFound => {},
+ else => |e| return e,
+ };
+
+ const stage2_target = try arena.create(stage1.Stage2Target);
+ stage2_target.* = .{
+ .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch
+ .os = @enumToInt(target.os.tag),
+ .abi = @enumToInt(target.abi),
+ .is_native_os = comp.bin_file.options.is_native_os,
+ .is_native_cpu = false, // Only true when bootstrapping the compiler.
+ .llvm_cpu_name = if (target.cpu.model.llvm_name) |s| s.ptr else null,
+ .llvm_cpu_features = comp.bin_file.options.llvm_cpu_features.?,
+ };
+ var progress: std.Progress = .{};
+ var main_progress_node = try progress.start("", null);
+ defer main_progress_node.end();
+ if (comp.color == .Off) progress.terminal = null;
+
+ comp.stage1_cache_manifest = &man;
+
+ const main_pkg_path = mod.root_pkg.root_src_directory.path orelse "";
+
+ const stage1_module = stage1.create(
+ @enumToInt(comp.bin_file.options.optimize_mode),
+ main_pkg_path.ptr,
+ main_pkg_path.len,
+ main_zig_file.ptr,
+ main_zig_file.len,
+ zig_lib_dir.ptr,
+ zig_lib_dir.len,
+ stage2_target,
+ comp.bin_file.options.is_test,
+ ) orelse return error.OutOfMemory;
+
+ const emit_bin_path = if (comp.bin_file.options.emit != null) blk: {
+ const bin_basename = try std.zig.binNameAlloc(arena, .{
+ .root_name = comp.bin_file.options.root_name,
+ .target = target,
+ .output_mode = .Obj,
+ });
+ break :blk try directory.join(arena, &[_][]const u8{bin_basename});
+ } else "";
+ if (comp.emit_h != null) {
+ log.warn("-femit-h is not available in the stage1 backend; no .h file will be produced", .{});
+ }
+ const emit_h_path = try stage1LocPath(arena, comp.emit_h, directory);
+ const emit_asm_path = try stage1LocPath(arena, comp.emit_asm, directory);
+ const emit_llvm_ir_path = try stage1LocPath(arena, comp.emit_llvm_ir, directory);
+ const emit_analysis_path = try stage1LocPath(arena, comp.emit_analysis, directory);
+ const emit_docs_path = try stage1LocPath(arena, comp.emit_docs, directory);
+ const stage1_pkg = try createStage1Pkg(arena, "root", mod.root_pkg, null);
+ const test_filter = comp.test_filter orelse ""[0..0];
+ const test_name_prefix = comp.test_name_prefix orelse ""[0..0];
+ const subsystem = if (comp.bin_file.options.subsystem) |s|
+ @intToEnum(stage1.TargetSubsystem, @enumToInt(s))
+ else
+ stage1.TargetSubsystem.Auto;
+ stage1_module.* = .{
+ .root_name_ptr = comp.bin_file.options.root_name.ptr,
+ .root_name_len = comp.bin_file.options.root_name.len,
+ .emit_o_ptr = emit_bin_path.ptr,
+ .emit_o_len = emit_bin_path.len,
+ .emit_h_ptr = emit_h_path.ptr,
+ .emit_h_len = emit_h_path.len,
+ .emit_asm_ptr = emit_asm_path.ptr,
+ .emit_asm_len = emit_asm_path.len,
+ .emit_llvm_ir_ptr = emit_llvm_ir_path.ptr,
+ .emit_llvm_ir_len = emit_llvm_ir_path.len,
+ .emit_analysis_json_ptr = emit_analysis_path.ptr,
+ .emit_analysis_json_len = emit_analysis_path.len,
+ .emit_docs_ptr = emit_docs_path.ptr,
+ .emit_docs_len = emit_docs_path.len,
+ .builtin_zig_path_ptr = builtin_zig_path.ptr,
+ .builtin_zig_path_len = builtin_zig_path.len,
+ .test_filter_ptr = test_filter.ptr,
+ .test_filter_len = test_filter.len,
+ .test_name_prefix_ptr = test_name_prefix.ptr,
+ .test_name_prefix_len = test_name_prefix.len,
+ .userdata = @ptrToInt(comp),
+ .root_pkg = stage1_pkg,
+ .code_model = @enumToInt(comp.bin_file.options.machine_code_model),
+ .subsystem = subsystem,
+ .err_color = @enumToInt(comp.color),
+ .pic = comp.bin_file.options.pic,
+ .link_libc = comp.bin_file.options.link_libc,
+ .link_libcpp = comp.bin_file.options.link_libcpp,
+ .strip = comp.bin_file.options.strip,
+ .is_single_threaded = comp.bin_file.options.single_threaded,
+ .dll_export_fns = comp.bin_file.options.dll_export_fns,
+ .link_mode_dynamic = comp.bin_file.options.link_mode == .Dynamic,
+ .valgrind_enabled = comp.bin_file.options.valgrind,
+ .function_sections = comp.bin_file.options.function_sections,
+ .enable_stack_probing = comp.bin_file.options.stack_check,
+ .enable_time_report = comp.time_report,
+ .enable_stack_report = comp.stack_report,
+ .test_is_evented = comp.test_evented_io,
+ .verbose_tokenize = comp.verbose_tokenize,
+ .verbose_ast = comp.verbose_ast,
+ .verbose_ir = comp.verbose_ir,
+ .verbose_llvm_ir = comp.verbose_llvm_ir,
+ .verbose_cimport = comp.verbose_cimport,
+ .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
+ .main_progress_node = main_progress_node,
+ .have_c_main = false,
+ .have_winmain = false,
+ .have_wwinmain = false,
+ .have_winmain_crt_startup = false,
+ .have_wwinmain_crt_startup = false,
+ .have_dllmain_crt_startup = false,
+ };
+
+ const inferred_lib_start_index = comp.bin_file.options.system_libs.count();
+ stage1_module.build_object();
+
+ if (comp.bin_file.options.system_libs.count() > inferred_lib_start_index) {
+ // We need to save the inferred link libs to the cache, otherwise if we get a cache hit
+ // next time we will be missing these libs.
+ var libs_txt = std.ArrayList(u8).init(arena);
+ for (comp.bin_file.options.system_libs.items()[inferred_lib_start_index..]) |entry| {
+ try libs_txt.writer().print("{s}\n", .{entry.key});
+ }
+ try directory.handle.writeFile(libs_txt_basename, libs_txt.items);
+ }
+
+ mod.stage1_flags = .{
+ .have_c_main = stage1_module.have_c_main,
+ .have_winmain = stage1_module.have_winmain,
+ .have_wwinmain = stage1_module.have_wwinmain,
+ .have_winmain_crt_startup = stage1_module.have_winmain_crt_startup,
+ .have_wwinmain_crt_startup = stage1_module.have_wwinmain_crt_startup,
+ .have_dllmain_crt_startup = stage1_module.have_dllmain_crt_startup,
+ };
+
+ stage1_module.destroy();
+
+ const digest = man.final();
+
+ // Update the dangling symlink with the digest. If it fails we can continue; it only
+ // means that the next invocation will have an unnecessary cache miss.
+ const stage1_flags_byte = @bitCast(u8, mod.stage1_flags);
+ log.debug("stage1 {} final digest={} flags={x}", .{
+ mod.root_pkg.root_src_path, digest, stage1_flags_byte,
+ });
+ var digest_plus_flags: [digest.len + 2]u8 = undefined;
+ digest_plus_flags[0..digest.len].* = digest;
+ assert(std.fmt.formatIntBuf(digest_plus_flags[digest.len..], stage1_flags_byte, 16, false, .{
+ .width = 2,
+ .fill = '0',
+ }) == 2);
+ log.debug("saved digest + flags: '{s}' (byte = {}) have_winmain_crt_startup={}", .{
+ digest_plus_flags, stage1_flags_byte, mod.stage1_flags.have_winmain_crt_startup,
+ });
+ directory.handle.symLink(&digest_plus_flags, id_symlink_basename, .{}) catch |err| {
+ log.warn("failed to save stage1 hash digest symlink: {}", .{@errorName(err)});
+ };
+ // Again failure here only means an unnecessary cache miss.
+ man.writeManifest() catch |err| {
+ log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)});
+ };
+ // We hang on to this lock so that the output file path can be used without
+ // other processes clobbering it.
+ comp.stage1_lock = man.toOwnedLock();
+}
+
+fn stage1LocPath(arena: *Allocator, opt_loc: ?EmitLoc, cache_directory: Directory) ![]const u8 {
+ const loc = opt_loc orelse return "";
+ const directory = loc.directory orelse cache_directory;
+ return directory.join(arena, &[_][]const u8{loc.basename});
+}
+
+fn createStage1Pkg(
+ arena: *Allocator,
+ name: []const u8,
+ pkg: *Package,
+ parent_pkg: ?*stage1.Pkg,
+) error{OutOfMemory}!*stage1.Pkg {
+ const child_pkg = try arena.create(stage1.Pkg);
+
+ const pkg_children = blk: {
+ var children = std.ArrayList(*stage1.Pkg).init(arena);
+ var it = pkg.table.iterator();
+ while (it.next()) |entry| {
+ try children.append(try createStage1Pkg(arena, entry.key, entry.value, child_pkg));
+ }
+ break :blk children.items;
+ };
+
+ const src_path = try pkg.root_src_directory.join(arena, &[_][]const u8{pkg.root_src_path});
+
+ child_pkg.* = .{
+ .name_ptr = name.ptr,
+ .name_len = name.len,
+ .path_ptr = src_path.ptr,
+ .path_len = src_path.len,
+ .children_ptr = pkg_children.ptr,
+ .children_len = pkg_children.len,
+ .parent = parent_pkg,
+ };
+ return child_pkg;
+}
+
+pub fn build_crt_file(
+ comp: *Compilation,
+ root_name: []const u8,
+ output_mode: std.builtin.OutputMode,
+ c_source_files: []const Compilation.CSourceFile,
+) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const target = comp.getTarget();
+ const basename = try std.zig.binNameAlloc(comp.gpa, .{
+ .root_name = root_name,
+ .target = target,
+ .output_mode = output_mode,
+ });
+ errdefer comp.gpa.free(basename);
+
+ // TODO: This is extracted into a local variable to work around a stage1 miscompilation.
+ const emit_bin = Compilation.EmitLoc{
+ .directory = null, // Put it in the cache directory.
+ .basename = basename,
+ };
+ const sub_compilation = try Compilation.create(comp.gpa, .{
+ .local_cache_directory = comp.global_cache_directory,
+ .global_cache_directory = comp.global_cache_directory,
+ .zig_lib_directory = comp.zig_lib_directory,
+ .target = target,
+ .root_name = root_name,
+ .root_pkg = null,
+ .output_mode = output_mode,
+ .rand = comp.rand,
+ .libc_installation = comp.bin_file.options.libc_installation,
+ .emit_bin = emit_bin,
+ .optimize_mode = comp.bin_file.options.optimize_mode,
+ .want_sanitize_c = false,
+ .want_stack_check = false,
+ .want_valgrind = false,
+ .want_pic = comp.bin_file.options.pic,
+ .emit_h = null,
+ .strip = comp.bin_file.options.strip,
+ .is_native_os = comp.bin_file.options.is_native_os,
+ .self_exe_path = comp.self_exe_path,
+ .c_source_files = c_source_files,
+ .verbose_cc = comp.verbose_cc,
+ .verbose_link = comp.bin_file.options.verbose_link,
+ .verbose_tokenize = comp.verbose_tokenize,
+ .verbose_ast = comp.verbose_ast,
+ .verbose_ir = comp.verbose_ir,
+ .verbose_llvm_ir = comp.verbose_llvm_ir,
+ .verbose_cimport = comp.verbose_cimport,
+ .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
+ .clang_passthrough_mode = comp.clang_passthrough_mode,
+ .is_compiler_rt_or_libc = true,
+ .parent_compilation_link_libc = comp.bin_file.options.link_libc,
+ });
+ defer sub_compilation.destroy();
+
+ try sub_compilation.updateSubCompilation();
+
+ try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1);
+
+ comp.crt_files.putAssumeCapacityNoClobber(basename, .{
+ .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{
+ sub_compilation.bin_file.options.emit.?.sub_path,
+ }),
+ .lock = sub_compilation.bin_file.toOwnedLock(),
+ });
+}
+
+pub fn stage1AddLinkLib(comp: *Compilation, lib_name: []const u8) !void {
+ // This happens when an `extern "foo"` function is referenced by the stage1 backend.
+ // If we haven't seen this library yet and we're targeting Windows, we need to queue up
+ // a work item to produce the DLL import library for this.
+ const gop = try comp.bin_file.options.system_libs.getOrPut(comp.gpa, lib_name);
+ if (!gop.found_existing and comp.getTarget().os.tag == .windows) {
+ try comp.work_queue.writeItem(.{
+ .windows_import_lib = comp.bin_file.options.system_libs.count() - 1,
+ });
+ }
+}
diff --git a/src-self-hosted/dep_tokenizer.zig b/src/DepTokenizer.zig
similarity index 51%
rename from src-self-hosted/dep_tokenizer.zig
rename to src/DepTokenizer.zig
index 20324cbf0c..cc2211a1aa 100644
--- a/src-self-hosted/dep_tokenizer.zig
+++ b/src/DepTokenizer.zig
@@ -1,361 +1,405 @@
+const Tokenizer = @This();
+
+index: usize = 0,
+bytes: []const u8,
+state: State = .lhs,
+
const std = @import("std");
const testing = std.testing;
+const assert = std.debug.assert;
-pub const Tokenizer = struct {
- arena: std.heap.ArenaAllocator,
- index: usize,
- bytes: []const u8,
- error_text: []const u8,
- state: State,
-
- pub fn init(allocator: *std.mem.Allocator, bytes: []const u8) Tokenizer {
- return Tokenizer{
- .arena = std.heap.ArenaAllocator.init(allocator),
- .index = 0,
- .bytes = bytes,
- .error_text = "",
- .state = State{ .lhs = {} },
- };
- }
-
- pub fn deinit(self: *Tokenizer) void {
- self.arena.deinit();
- }
-
- pub fn next(self: *Tokenizer) Error!?Token {
- while (self.index < self.bytes.len) {
- const char = self.bytes[self.index];
- while (true) {
- switch (self.state) {
- .lhs => switch (char) {
- '\t', '\n', '\r', ' ' => {
- // silently ignore whitespace
- break; // advance
- },
- else => {
- self.state = State{ .target = try std.ArrayListSentineled(u8, 0).initSize(&self.arena.allocator, 0) };
- },
- },
- .target => |*target| switch (char) {
- '\t', '\n', '\r', ' ' => {
- return self.errorIllegalChar(self.index, char, "invalid target", .{});
- },
- '$' => {
- self.state = State{ .target_dollar_sign = target.* };
- break; // advance
- },
- '\\' => {
- self.state = State{ .target_reverse_solidus = target.* };
- break; // advance
- },
- ':' => {
- self.state = State{ .target_colon = target.* };
- break; // advance
- },
- else => {
- try target.append(char);
- break; // advance
- },
- },
- .target_reverse_solidus => |*target| switch (char) {
- '\t', '\n', '\r' => {
- return self.errorIllegalChar(self.index, char, "bad target escape", .{});
- },
- ' ', '#', '\\' => {
- try target.append(char);
- self.state = State{ .target = target.* };
- break; // advance
- },
- '$' => {
- try target.appendSlice(self.bytes[self.index - 1 .. self.index]);
- self.state = State{ .target_dollar_sign = target.* };
- break; // advance
- },
- else => {
- try target.appendSlice(self.bytes[self.index - 1 .. self.index + 1]);
- self.state = State{ .target = target.* };
- break; // advance
- },
- },
- .target_dollar_sign => |*target| switch (char) {
- '$' => {
- try target.append(char);
- self.state = State{ .target = target.* };
- break; // advance
- },
- else => {
- return self.errorIllegalChar(self.index, char, "expecting '$'", .{});
- },
- },
- .target_colon => |*target| switch (char) {
- '\n', '\r' => {
- const bytes = target.span();
- if (bytes.len != 0) {
- self.state = State{ .lhs = {} };
- return Token{ .id = .target, .bytes = bytes };
- }
- // silently ignore null target
- self.state = State{ .lhs = {} };
- continue;
- },
- '\\' => {
- self.state = State{ .target_colon_reverse_solidus = target.* };
- break; // advance
- },
- else => {
- const bytes = target.span();
- if (bytes.len != 0) {
- self.state = State{ .rhs = {} };
- return Token{ .id = .target, .bytes = bytes };
- }
- // silently ignore null target
- self.state = State{ .lhs = {} };
- continue;
- },
- },
- .target_colon_reverse_solidus => |*target| switch (char) {
- '\n', '\r' => {
- const bytes = target.span();
- if (bytes.len != 0) {
- self.state = State{ .lhs = {} };
- return Token{ .id = .target, .bytes = bytes };
- }
- // silently ignore null target
- self.state = State{ .lhs = {} };
- continue;
- },
- else => {
- try target.appendSlice(self.bytes[self.index - 2 .. self.index + 1]);
- self.state = State{ .target = target.* };
- break;
- },
- },
- .rhs => switch (char) {
- '\t', ' ' => {
- // silently ignore horizontal whitespace
- break; // advance
- },
- '\n', '\r' => {
- self.state = State{ .lhs = {} };
- continue;
- },
- '\\' => {
- self.state = State{ .rhs_continuation = {} };
- break; // advance
- },
- '"' => {
- self.state = State{ .prereq_quote = try std.ArrayListSentineled(u8, 0).initSize(&self.arena.allocator, 0) };
- break; // advance
- },
- else => {
- self.state = State{ .prereq = try std.ArrayListSentineled(u8, 0).initSize(&self.arena.allocator, 0) };
- },
- },
- .rhs_continuation => switch (char) {
- '\n' => {
- self.state = State{ .rhs = {} };
- break; // advance
- },
- '\r' => {
- self.state = State{ .rhs_continuation_linefeed = {} };
- break; // advance
- },
- else => {
- return self.errorIllegalChar(self.index, char, "continuation expecting end-of-line", .{});
- },
- },
- .rhs_continuation_linefeed => switch (char) {
- '\n' => {
- self.state = State{ .rhs = {} };
- break; // advance
- },
- else => {
- return self.errorIllegalChar(self.index, char, "continuation expecting end-of-line", .{});
- },
- },
- .prereq_quote => |*prereq| switch (char) {
- '"' => {
- const bytes = prereq.span();
- self.index += 1;
- self.state = State{ .rhs = {} };
- return Token{ .id = .prereq, .bytes = bytes };
- },
- else => {
- try prereq.append(char);
- break; // advance
- },
- },
- .prereq => |*prereq| switch (char) {
- '\t', ' ' => {
- const bytes = prereq.span();
- self.state = State{ .rhs = {} };
- return Token{ .id = .prereq, .bytes = bytes };
- },
- '\n', '\r' => {
- const bytes = prereq.span();
- self.state = State{ .lhs = {} };
- return Token{ .id = .prereq, .bytes = bytes };
- },
- '\\' => {
- self.state = State{ .prereq_continuation = prereq.* };
- break; // advance
- },
- else => {
- try prereq.append(char);
- break; // advance
- },
- },
- .prereq_continuation => |*prereq| switch (char) {
- '\n' => {
- const bytes = prereq.span();
- self.index += 1;
- self.state = State{ .rhs = {} };
- return Token{ .id = .prereq, .bytes = bytes };
- },
- '\r' => {
- self.state = State{ .prereq_continuation_linefeed = prereq.* };
- break; // advance
- },
- else => {
- // not continuation
- try prereq.appendSlice(self.bytes[self.index - 1 .. self.index + 1]);
- self.state = State{ .prereq = prereq.* };
- break; // advance
- },
- },
- .prereq_continuation_linefeed => |prereq| switch (char) {
- '\n' => {
- const bytes = prereq.span();
- self.index += 1;
- self.state = State{ .rhs = {} };
- return Token{ .id = .prereq, .bytes = bytes };
- },
- else => {
- return self.errorIllegalChar(self.index, char, "continuation expecting end-of-line", .{});
- },
- },
- }
- }
- self.index += 1;
+pub fn next(self: *Tokenizer) ?Token {
+ var start = self.index;
+ var must_resolve = false;
+ while (self.index < self.bytes.len) {
+ const char = self.bytes[self.index];
+ switch (self.state) {
+ .lhs => switch (char) {
+ '\t', '\n', '\r', ' ' => {
+ // silently ignore whitespace
+ self.index += 1;
+ },
+ else => {
+ start = self.index;
+ self.state = .target;
+ },
+ },
+ .target => switch (char) {
+ '\t', '\n', '\r', ' ' => {
+ return errorIllegalChar(.invalid_target, self.index, char);
+ },
+ '$' => {
+ self.state = .target_dollar_sign;
+ self.index += 1;
+ },
+ '\\' => {
+ self.state = .target_reverse_solidus;
+ self.index += 1;
+ },
+ ':' => {
+ self.state = .target_colon;
+ self.index += 1;
+ },
+ else => {
+ self.index += 1;
+ },
+ },
+ .target_reverse_solidus => switch (char) {
+ '\t', '\n', '\r' => {
+ return errorIllegalChar(.bad_target_escape, self.index, char);
+ },
+ ' ', '#', '\\' => {
+ must_resolve = true;
+ self.state = .target;
+ self.index += 1;
+ },
+ '$' => {
+ self.state = .target_dollar_sign;
+ self.index += 1;
+ },
+ else => {
+ self.state = .target;
+ self.index += 1;
+ },
+ },
+ .target_dollar_sign => switch (char) {
+ '$' => {
+ must_resolve = true;
+ self.state = .target;
+ self.index += 1;
+ },
+ else => {
+ return errorIllegalChar(.expected_dollar_sign, self.index, char);
+ },
+ },
+ .target_colon => switch (char) {
+ '\n', '\r' => {
+ const bytes = self.bytes[start .. self.index - 1];
+ if (bytes.len != 0) {
+ self.state = .lhs;
+ return finishTarget(must_resolve, bytes);
+ }
+ // silently ignore null target
+ self.state = .lhs;
+ },
+ '\\' => {
+ self.state = .target_colon_reverse_solidus;
+ self.index += 1;
+ },
+ else => {
+ const bytes = self.bytes[start .. self.index - 1];
+ if (bytes.len != 0) {
+ self.state = .rhs;
+ return finishTarget(must_resolve, bytes);
+ }
+ // silently ignore null target
+ self.state = .lhs;
+ },
+ },
+ .target_colon_reverse_solidus => switch (char) {
+ '\n', '\r' => {
+ const bytes = self.bytes[start .. self.index - 2];
+ if (bytes.len != 0) {
+ self.state = .lhs;
+ return finishTarget(must_resolve, bytes);
+ }
+ // silently ignore null target
+ self.state = .lhs;
+ },
+ else => {
+ self.state = .target;
+ },
+ },
+ .rhs => switch (char) {
+ '\t', ' ' => {
+ // silently ignore horizontal whitespace
+ self.index += 1;
+ },
+ '\n', '\r' => {
+ self.state = .lhs;
+ },
+ '\\' => {
+ self.state = .rhs_continuation;
+ self.index += 1;
+ },
+ '"' => {
+ self.state = .prereq_quote;
+ self.index += 1;
+ start = self.index;
+ },
+ else => {
+ start = self.index;
+ self.state = .prereq;
+ },
+ },
+ .rhs_continuation => switch (char) {
+ '\n' => {
+ self.state = .rhs;
+ self.index += 1;
+ },
+ '\r' => {
+ self.state = .rhs_continuation_linefeed;
+ self.index += 1;
+ },
+ else => {
+ return errorIllegalChar(.continuation_eol, self.index, char);
+ },
+ },
+ .rhs_continuation_linefeed => switch (char) {
+ '\n' => {
+ self.state = .rhs;
+ self.index += 1;
+ },
+ else => {
+ return errorIllegalChar(.continuation_eol, self.index, char);
+ },
+ },
+ .prereq_quote => switch (char) {
+ '"' => {
+ self.index += 1;
+ self.state = .rhs;
+ return Token{ .prereq = self.bytes[start .. self.index - 1] };
+ },
+ else => {
+ self.index += 1;
+ },
+ },
+ .prereq => switch (char) {
+ '\t', ' ' => {
+ self.state = .rhs;
+ return Token{ .prereq = self.bytes[start..self.index] };
+ },
+ '\n', '\r' => {
+ self.state = .lhs;
+ return Token{ .prereq = self.bytes[start..self.index] };
+ },
+ '\\' => {
+ self.state = .prereq_continuation;
+ self.index += 1;
+ },
+ else => {
+ self.index += 1;
+ },
+ },
+ .prereq_continuation => switch (char) {
+ '\n' => {
+ self.index += 1;
+ self.state = .rhs;
+ return Token{ .prereq = self.bytes[start .. self.index - 2] };
+ },
+ '\r' => {
+ self.state = .prereq_continuation_linefeed;
+ self.index += 1;
+ },
+ else => {
+ // not continuation
+ self.state = .prereq;
+ self.index += 1;
+ },
+ },
+ .prereq_continuation_linefeed => switch (char) {
+ '\n' => {
+ self.index += 1;
+ self.state = .rhs;
+ return Token{ .prereq = self.bytes[start .. self.index - 1] };
+ },
+ else => {
+ return errorIllegalChar(.continuation_eol, self.index, char);
+ },
+ },
}
-
- // eof, handle maybe incomplete token
- if (self.index == 0) return null;
- const idx = self.index - 1;
+ } else {
switch (self.state) {
.lhs,
.rhs,
.rhs_continuation,
.rhs_continuation_linefeed,
- => {},
- .target => |target| {
- return self.errorPosition(idx, target.span(), "incomplete target", .{});
+ => return null,
+ .target => {
+ return errorPosition(.incomplete_target, start, self.bytes[start..]);
},
.target_reverse_solidus,
.target_dollar_sign,
=> {
- const index = self.index - 1;
- return self.errorIllegalChar(idx, self.bytes[idx], "incomplete escape", .{});
+ const idx = self.index - 1;
+ return errorIllegalChar(.incomplete_escape, idx, self.bytes[idx]);
},
- .target_colon => |target| {
- const bytes = target.span();
+ .target_colon => {
+ const bytes = self.bytes[start .. self.index - 1];
if (bytes.len != 0) {
self.index += 1;
- self.state = State{ .rhs = {} };
- return Token{ .id = .target, .bytes = bytes };
+ self.state = .rhs;
+ return finishTarget(must_resolve, bytes);
}
// silently ignore null target
- self.state = State{ .lhs = {} };
+ self.state = .lhs;
+ return null;
},
- .target_colon_reverse_solidus => |target| {
- const bytes = target.span();
+ .target_colon_reverse_solidus => {
+ const bytes = self.bytes[start .. self.index - 2];
if (bytes.len != 0) {
self.index += 1;
- self.state = State{ .rhs = {} };
- return Token{ .id = .target, .bytes = bytes };
+ self.state = .rhs;
+ return finishTarget(must_resolve, bytes);
}
// silently ignore null target
- self.state = State{ .lhs = {} };
+ self.state = .lhs;
+ return null;
},
- .prereq_quote => |prereq| {
- return self.errorPosition(idx, prereq.span(), "incomplete quoted prerequisite", .{});
+ .prereq_quote => {
+ return errorPosition(.incomplete_quoted_prerequisite, start, self.bytes[start..]);
},
- .prereq => |prereq| {
- const bytes = prereq.span();
- self.state = State{ .lhs = {} };
- return Token{ .id = .prereq, .bytes = bytes };
+ .prereq => {
+ self.state = .lhs;
+ return Token{ .prereq = self.bytes[start..] };
},
- .prereq_continuation => |prereq| {
- const bytes = prereq.span();
- self.state = State{ .lhs = {} };
- return Token{ .id = .prereq, .bytes = bytes };
+ .prereq_continuation => {
+ self.state = .lhs;
+ return Token{ .prereq = self.bytes[start .. self.index - 1] };
},
- .prereq_continuation_linefeed => |prereq| {
- const bytes = prereq.span();
- self.state = State{ .lhs = {} };
- return Token{ .id = .prereq, .bytes = bytes };
+ .prereq_continuation_linefeed => {
+ self.state = .lhs;
+ return Token{ .prereq = self.bytes[start .. self.index - 2] };
},
}
- return null;
}
+ unreachable;
+}
- fn errorf(self: *Tokenizer, comptime fmt: []const u8, args: anytype) Error {
- self.error_text = try std.fmt.allocPrintZ(&self.arena.allocator, fmt, args);
- return Error.InvalidInput;
- }
+fn errorPosition(comptime id: @TagType(Token), index: usize, bytes: []const u8) Token {
+ return @unionInit(Token, @tagName(id), .{ .index = index, .bytes = bytes });
+}
- fn errorPosition(self: *Tokenizer, position: usize, bytes: []const u8, comptime fmt: []const u8, args: anytype) Error {
- var buffer = try std.ArrayListSentineled(u8, 0).initSize(&self.arena.allocator, 0);
- try buffer.outStream().print(fmt, args);
- try buffer.appendSlice(" '");
- var out = makeOutput(std.ArrayListSentineled(u8, 0).appendSlice, &buffer);
- try printCharValues(&out, bytes);
- try buffer.appendSlice("'");
- try buffer.outStream().print(" at position {}", .{position - (bytes.len - 1)});
- self.error_text = buffer.span();
- return Error.InvalidInput;
- }
+fn errorIllegalChar(comptime id: @TagType(Token), index: usize, char: u8) Token {
+ return @unionInit(Token, @tagName(id), .{ .index = index, .char = char });
+}
- fn errorIllegalChar(self: *Tokenizer, position: usize, char: u8, comptime fmt: []const u8, args: anytype) Error {
- var buffer = try std.ArrayListSentineled(u8, 0).initSize(&self.arena.allocator, 0);
- try buffer.appendSlice("illegal char ");
- try printUnderstandableChar(&buffer, char);
- try buffer.outStream().print(" at position {}", .{position});
- if (fmt.len != 0) try buffer.outStream().print(": " ++ fmt, args);
- self.error_text = buffer.span();
- return Error.InvalidInput;
- }
+fn finishTarget(must_resolve: bool, bytes: []const u8) Token {
+ return if (must_resolve)
+ .{ .target_must_resolve = bytes }
+ else
+ .{ .target = bytes };
+}
- const Error = error{
- OutOfMemory,
- InvalidInput,
+const State = enum {
+ lhs,
+ target,
+ target_reverse_solidus,
+ target_dollar_sign,
+ target_colon,
+ target_colon_reverse_solidus,
+ rhs,
+ rhs_continuation,
+ rhs_continuation_linefeed,
+ prereq_quote,
+ prereq,
+ prereq_continuation,
+ prereq_continuation_linefeed,
+};
+
+pub const Token = union(enum) {
+ target: []const u8,
+ target_must_resolve: []const u8,
+ prereq: []const u8,
+
+ incomplete_quoted_prerequisite: IndexAndBytes,
+ incomplete_target: IndexAndBytes,
+
+ invalid_target: IndexAndChar,
+ bad_target_escape: IndexAndChar,
+ expected_dollar_sign: IndexAndChar,
+ continuation_eol: IndexAndChar,
+ incomplete_escape: IndexAndChar,
+
+ pub const IndexAndChar = struct {
+ index: usize,
+ char: u8,
};
- const State = union(enum) {
- lhs: void,
- target: std.ArrayListSentineled(u8, 0),
- target_reverse_solidus: std.ArrayListSentineled(u8, 0),
- target_dollar_sign: std.ArrayListSentineled(u8, 0),
- target_colon: std.ArrayListSentineled(u8, 0),
- target_colon_reverse_solidus: std.ArrayListSentineled(u8, 0),
- rhs: void,
- rhs_continuation: void,
- rhs_continuation_linefeed: void,
- prereq_quote: std.ArrayListSentineled(u8, 0),
- prereq: std.ArrayListSentineled(u8, 0),
- prereq_continuation: std.ArrayListSentineled(u8, 0),
- prereq_continuation_linefeed: std.ArrayListSentineled(u8, 0),
- };
-
- const Token = struct {
- id: ID,
+ pub const IndexAndBytes = struct {
+ index: usize,
bytes: []const u8,
-
- const ID = enum {
- target,
- prereq,
- };
};
+
+ /// Resolve escapes in target. Only valid with .target_must_resolve.
+ pub fn resolve(self: Token, writer: anytype) @TypeOf(writer).Error!void {
+ const bytes = self.target_must_resolve; // resolve called on incorrect token
+
+ var state: enum { start, escape, dollar } = .start;
+ for (bytes) |c| {
+ switch (state) {
+ .start => {
+ switch (c) {
+ '\\' => state = .escape,
+ '$' => state = .dollar,
+ else => try writer.writeByte(c),
+ }
+ },
+ .escape => {
+ switch (c) {
+ ' ', '#', '\\' => {},
+ '$' => {
+ try writer.writeByte('\\');
+ state = .dollar;
+ continue;
+ },
+ else => try writer.writeByte('\\'),
+ }
+ try writer.writeByte(c);
+ state = .start;
+ },
+ .dollar => {
+ try writer.writeByte('$');
+ switch (c) {
+ '$' => {},
+ else => try writer.writeByte(c),
+ }
+ state = .start;
+ },
+ }
+ }
+ }
+
+ pub fn printError(self: Token, writer: anytype) @TypeOf(writer).Error!void {
+ switch (self) {
+ .target, .target_must_resolve, .prereq => unreachable, // not an error
+ .incomplete_quoted_prerequisite,
+ .incomplete_target,
+ => |index_and_bytes| {
+ try writer.print("{} '", .{self.errStr()});
+ if (self == .incomplete_target) {
+ const tmp = Token{ .target_must_resolve = index_and_bytes.bytes };
+ try tmp.resolve(writer);
+ } else {
+ try printCharValues(writer, index_and_bytes.bytes);
+ }
+ try writer.print("' at position {}", .{index_and_bytes.index});
+ },
+ .invalid_target,
+ .bad_target_escape,
+ .expected_dollar_sign,
+ .continuation_eol,
+ .incomplete_escape,
+ => |index_and_char| {
+ try writer.writeAll("illegal char ");
+ try printUnderstandableChar(writer, index_and_char.char);
+ try writer.print(" at position {}: {}", .{ index_and_char.index, self.errStr() });
+ },
+ }
+ }
+
+ fn errStr(self: Token) []const u8 {
+ return switch (self) {
+ .target, .target_must_resolve, .prereq => unreachable, // not an error
+ .incomplete_quoted_prerequisite => "incomplete quoted prerequisite",
+ .incomplete_target => "incomplete target",
+ .invalid_target => "invalid target",
+ .bad_target_escape => "bad target escape",
+ .expected_dollar_sign => "expecting '$'",
+ .continuation_eol => "continuation expecting end-of-line",
+ .incomplete_escape => "incomplete escape",
+ };
+ }
};
test "empty file" {
@@ -750,16 +794,16 @@ test "error incomplete target" {
);
try depTokenizer("\\ foo.o",
- \\ERROR: incomplete target ' foo.o' at position 1
+ \\ERROR: incomplete target ' foo.o' at position 0
);
try depTokenizer("\\#foo.o",
- \\ERROR: incomplete target '#foo.o' at position 1
+ \\ERROR: incomplete target '#foo.o' at position 0
);
try depTokenizer("\\\\foo.o",
- \\ERROR: incomplete target '\foo.o' at position 1
+ \\ERROR: incomplete target '\foo.o' at position 0
);
try depTokenizer("$$foo.o",
- \\ERROR: incomplete target '$foo.o' at position 1
+ \\ERROR: incomplete target '$foo.o' at position 0
);
}
@@ -836,33 +880,40 @@ test "error prereq - continuation expecting end-of-line" {
// - tokenize input, emit textual representation, and compare to expect
fn depTokenizer(input: []const u8, expect: []const u8) !void {
- var arena_allocator = std.heap.ArenaAllocator.init(std.heap.page_allocator);
+ var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
const arena = &arena_allocator.allocator;
defer arena_allocator.deinit();
- var it = Tokenizer.init(arena, input);
+ var it: Tokenizer = .{ .bytes = input };
var buffer = try std.ArrayListSentineled(u8, 0).initSize(arena, 0);
+ var resolve_buf = std.ArrayList(u8).init(arena);
var i: usize = 0;
- while (true) {
- const r = it.next() catch |err| {
- switch (err) {
- Tokenizer.Error.InvalidInput => {
- if (i != 0) try buffer.appendSlice("\n");
- try buffer.appendSlice("ERROR: ");
- try buffer.appendSlice(it.error_text);
- },
- else => return err,
- }
- break;
- };
- const token = r orelse break;
+ while (it.next()) |token| {
if (i != 0) try buffer.appendSlice("\n");
- try buffer.appendSlice(@tagName(token.id));
- try buffer.appendSlice(" = {");
- for (token.bytes) |b| {
- try buffer.append(printable_char_tab[b]);
+ switch (token) {
+ .target, .prereq => |bytes| {
+ try buffer.appendSlice(@tagName(token));
+ try buffer.appendSlice(" = {");
+ for (bytes) |b| {
+ try buffer.append(printable_char_tab[b]);
+ }
+ try buffer.appendSlice("}");
+ },
+ .target_must_resolve => {
+ try buffer.appendSlice("target = {");
+ try token.resolve(resolve_buf.writer());
+ for (resolve_buf.items) |b| {
+ try buffer.append(printable_char_tab[b]);
+ }
+ resolve_buf.items.len = 0;
+ try buffer.appendSlice("}");
+ },
+ else => {
+ try buffer.appendSlice("ERROR: ");
+ try token.printError(buffer.outStream());
+ break;
+ },
}
- try buffer.appendSlice("}");
i += 1;
}
const got: []const u8 = buffer.span();
@@ -872,13 +923,13 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void {
return;
}
- var out = makeOutput(std.fs.File.write, try std.io.getStdErr());
+ const out = std.io.getStdErr().writer();
- try out.write("\n");
- try printSection(&out, "<<<< input", input);
- try printSection(&out, "==== expect", expect);
- try printSection(&out, ">>>> got", got);
- try printRuler(&out);
+ try out.writeAll("\n");
+ try printSection(out, "<<<< input", input);
+ try printSection(out, "==== expect", expect);
+ try printSection(out, ">>>> got", got);
+ try printRuler(out);
testing.expect(false);
}
@@ -887,29 +938,29 @@ fn printSection(out: anytype, label: []const u8, bytes: []const u8) !void {
try printLabel(out, label, bytes);
try hexDump(out, bytes);
try printRuler(out);
- try out.write(bytes);
- try out.write("\n");
+ try out.writeAll(bytes);
+ try out.writeAll("\n");
}
fn printLabel(out: anytype, label: []const u8, bytes: []const u8) !void {
var buf: [80]u8 = undefined;
var text = try std.fmt.bufPrint(buf[0..], "{} {} bytes ", .{ label, bytes.len });
- try out.write(text);
+ try out.writeAll(text);
var i: usize = text.len;
const end = 79;
while (i < 79) : (i += 1) {
- try out.write([_]u8{label[0]});
+ try out.writeAll(&[_]u8{label[0]});
}
- try out.write("\n");
+ try out.writeAll("\n");
}
fn printRuler(out: anytype) !void {
var i: usize = 0;
const end = 79;
while (i < 79) : (i += 1) {
- try out.write("-");
+ try out.writeAll("-");
}
- try out.write("\n");
+ try out.writeAll("\n");
}
fn hexDump(out: anytype, bytes: []const u8) !void {
@@ -924,116 +975,90 @@ fn hexDump(out: anytype, bytes: []const u8) !void {
const n = bytes.len & 0x0f;
if (n > 0) {
try printDecValue(out, offset, 8);
- try out.write(":");
- try out.write(" ");
+ try out.writeAll(":");
+ try out.writeAll(" ");
var end1 = std.math.min(offset + n, offset + 8);
for (bytes[offset..end1]) |b| {
- try out.write(" ");
+ try out.writeAll(" ");
try printHexValue(out, b, 2);
}
var end2 = offset + n;
if (end2 > end1) {
- try out.write(" ");
+ try out.writeAll(" ");
for (bytes[end1..end2]) |b| {
- try out.write(" ");
+ try out.writeAll(" ");
try printHexValue(out, b, 2);
}
}
const short = 16 - n;
var i: usize = 0;
while (i < short) : (i += 1) {
- try out.write(" ");
+ try out.writeAll(" ");
}
if (end2 > end1) {
- try out.write(" |");
+ try out.writeAll(" |");
} else {
- try out.write(" |");
+ try out.writeAll(" |");
}
try printCharValues(out, bytes[offset..end2]);
- try out.write("|\n");
+ try out.writeAll("|\n");
offset += n;
}
try printDecValue(out, offset, 8);
- try out.write(":");
- try out.write("\n");
+ try out.writeAll(":");
+ try out.writeAll("\n");
}
fn hexDump16(out: anytype, offset: usize, bytes: []const u8) !void {
try printDecValue(out, offset, 8);
- try out.write(":");
- try out.write(" ");
+ try out.writeAll(":");
+ try out.writeAll(" ");
for (bytes[0..8]) |b| {
- try out.write(" ");
+ try out.writeAll(" ");
try printHexValue(out, b, 2);
}
- try out.write(" ");
+ try out.writeAll(" ");
for (bytes[8..16]) |b| {
- try out.write(" ");
+ try out.writeAll(" ");
try printHexValue(out, b, 2);
}
- try out.write(" |");
+ try out.writeAll(" |");
try printCharValues(out, bytes);
- try out.write("|\n");
+ try out.writeAll("|\n");
}
fn printDecValue(out: anytype, value: u64, width: u8) !void {
var buffer: [20]u8 = undefined;
- const len = std.fmt.formatIntBuf(buffer[0..], value, 10, false, width);
- try out.write(buffer[0..len]);
+ const len = std.fmt.formatIntBuf(buffer[0..], value, 10, false, .{ .width = width, .fill = '0' });
+ try out.writeAll(buffer[0..len]);
}
fn printHexValue(out: anytype, value: u64, width: u8) !void {
var buffer: [16]u8 = undefined;
- const len = std.fmt.formatIntBuf(buffer[0..], value, 16, false, width);
- try out.write(buffer[0..len]);
+ const len = std.fmt.formatIntBuf(buffer[0..], value, 16, false, .{ .width = width, .fill = '0' });
+ try out.writeAll(buffer[0..len]);
}
fn printCharValues(out: anytype, bytes: []const u8) !void {
for (bytes) |b| {
- try out.write(&[_]u8{printable_char_tab[b]});
+ try out.writeAll(&[_]u8{printable_char_tab[b]});
}
}
-fn printUnderstandableChar(buffer: *std.ArrayListSentineled(u8, 0), char: u8) !void {
+fn printUnderstandableChar(out: anytype, char: u8) !void {
if (!std.ascii.isPrint(char) or char == ' ') {
- try buffer.outStream().print("\\x{X:2}", .{char});
+ try out.print("\\x{X:0>2}", .{char});
} else {
- try buffer.appendSlice("'");
- try buffer.append(printable_char_tab[char]);
- try buffer.appendSlice("'");
+ try out.print("'{c}'", .{printable_char_tab[char]});
}
}
// zig fmt: off
-const printable_char_tab: []const u8 =
+const printable_char_tab: [256]u8 = (
"................................ !\"#$%&'()*+,-./0123456789:;<=>?" ++
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~." ++
"................................................................" ++
- "................................................................";
-// zig fmt: on
-comptime {
- std.debug.assert(printable_char_tab.len == 256);
-}
+ "................................................................"
+).*;
-// Make an output var that wraps a context and output function.
-// output: must be a function that takes a `self` idiom parameter
-// and a bytes parameter
-// context: must be that self
-fn makeOutput(comptime output: anytype, context: anytype) Output(output, @TypeOf(context)) {
- return Output(output, @TypeOf(context)){
- .context = context,
- };
-}
-
-fn Output(comptime output_func: anytype, comptime Context: type) type {
- return struct {
- context: Context,
-
- pub const output = output_func;
-
- fn write(self: @This(), bytes: []const u8) !void {
- try output_func(self.context, bytes);
- }
- };
-}
diff --git a/src-self-hosted/Module.zig b/src/Module.zig
similarity index 87%
rename from src-self-hosted/Module.zig
rename to src/Module.zig
index dc48ae23e7..4fcf72f4ff 100644
--- a/src-self-hosted/Module.zig
+++ b/src/Module.zig
@@ -1,4 +1,6 @@
+const Module = @This();
const std = @import("std");
+const Compilation = @import("Compilation.zig");
const mem = std.mem;
const Allocator = std.mem.Allocator;
const ArrayListUnmanaged = std.ArrayListUnmanaged;
@@ -14,25 +16,24 @@ const Package = @import("Package.zig");
const link = @import("link.zig");
const ir = @import("ir.zig");
const zir = @import("zir.zig");
-const Module = @This();
const Inst = ir.Inst;
const Body = ir.Body;
const ast = std.zig.ast;
const trace = @import("tracy.zig").trace;
-const liveness = @import("liveness.zig");
const astgen = @import("astgen.zig");
const zir_sema = @import("zir_sema.zig");
/// General-purpose allocator. Used for both temporary and long-term storage.
gpa: *Allocator,
-/// Pointer to externally managed resource.
+comp: *Compilation,
+
+/// Where our incremental compilation metadata serialization will go.
+zig_cache_artifact_directory: Compilation.Directory,
+/// Pointer to externally managed resource. `null` if there is no zig file being compiled.
root_pkg: *Package,
/// Module owns this resource.
/// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`.
root_scope: *Scope,
-bin_file: *link.File,
-bin_file_dir: std.fs.Dir,
-bin_file_path: []const u8,
/// It's rare for a decl to be exported, so we save memory by having a sparse map of
/// Decl pointers to details about them being exported.
/// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table.
@@ -47,55 +48,42 @@ symbol_exports: std.StringArrayHashMapUnmanaged(*Export) = .{},
export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},
/// Maps fully qualified namespaced names to the Decl struct for them.
decl_table: std.ArrayHashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{},
-
-link_error_flags: link.File.ErrorFlags = .{},
-
-work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
-
/// We optimize memory usage for a compilation with no compile errors by storing the
/// error messages and mapping outside of `Decl`.
-/// The ErrorMsg memory is owned by the decl, using Module's allocator.
+/// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator.
/// Note that a Decl can succeed but the Fn it represents can fail. In this case,
/// a Decl can have a failed_decls entry but have analysis status of success.
-failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{},
+failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *Compilation.ErrorMsg) = .{},
/// Using a map here for consistency with the other fields here.
-/// The ErrorMsg memory is owned by the `Scope`, using Module's allocator.
-failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *ErrorMsg) = .{},
+/// The ErrorMsg memory is owned by the `Scope`, using Module's general purpose allocator.
+failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *Compilation.ErrorMsg) = .{},
/// Using a map here for consistency with the other fields here.
-/// The ErrorMsg memory is owned by the `Export`, using Module's allocator.
-failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *ErrorMsg) = .{},
+/// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator.
+failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *Compilation.ErrorMsg) = .{},
+
+next_anon_name_index: usize = 0,
+
+/// Candidates for deletion. After a semantic analysis update completes, this list
+/// contains Decls that need to be deleted if they end up having no references to them.
+deletion_set: ArrayListUnmanaged(*Decl) = .{},
+
+/// Error tags and their values, tag names are duped with mod.gpa.
+global_error_set: std.StringHashMapUnmanaged(u16) = .{},
/// Incrementing integer used to compare against the corresponding Decl
/// field to determine whether a Decl's status applies to an ongoing update, or a
/// previous analysis.
generation: u32 = 0,
-next_anon_name_index: usize = 0,
-
-/// Candidates for deletion. After a semantic analysis update completes, this list
-/// contains Decls that need to be deleted if they end up having no references to them.
-deletion_set: std.ArrayListUnmanaged(*Decl) = .{},
-
-/// Owned by Module.
-root_name: []u8,
-keep_source_files_loaded: bool,
-
-/// Error tags and their values, tag names are duped with mod.gpa.
-global_error_set: std.StringHashMapUnmanaged(u16) = .{},
-
-pub const InnerError = error{ OutOfMemory, AnalysisFail };
-
-const WorkItem = union(enum) {
- /// Write the machine code for a Decl to the output file.
- codegen_decl: *Decl,
- /// The Decl needs to be analyzed and possibly export itself.
- /// It may have already be analyzed, or it may have been determined
- /// to be outdated; in this case perform semantic analysis again.
- analyze_decl: *Decl,
- /// The source file containing the Decl has been updated, and so the
- /// Decl may need its line number information updated in the debug info.
- update_line_number: *Decl,
-};
+stage1_flags: packed struct {
+ have_winmain: bool = false,
+ have_wwinmain: bool = false,
+ have_winmain_crt_startup: bool = false,
+ have_wwinmain_crt_startup: bool = false,
+ have_dllmain_crt_startup: bool = false,
+ have_c_main: bool = false,
+ reserved: u2 = 0,
+} = .{},
pub const Export = struct {
options: std.builtin.ExportOptions,
@@ -622,7 +610,7 @@ pub const Scope = struct {
pub fn getSource(self: *File, module: *Module) ![:0]const u8 {
switch (self.source) {
.unloaded => {
- const source = try module.root_pkg.root_src_dir.readFileAllocOptions(
+ const source = try module.root_pkg.root_src_directory.handle.readFileAllocOptions(
module.gpa,
self.sub_file_path,
std.math.maxInt(u32),
@@ -720,7 +708,7 @@ pub const Scope = struct {
pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 {
switch (self.source) {
.unloaded => {
- const source = try module.root_pkg.root_src_dir.readFileAllocOptions(
+ const source = try module.root_pkg.root_src_directory.handle.readFileAllocOptions(
module.gpa,
self.sub_file_path,
std.math.maxInt(u32),
@@ -818,117 +806,14 @@ pub const Scope = struct {
};
};
-pub const AllErrors = struct {
- arena: std.heap.ArenaAllocator.State,
- list: []const Message,
-
- pub const Message = struct {
- src_path: []const u8,
- line: usize,
- column: usize,
- byte_offset: usize,
- msg: []const u8,
- };
-
- pub fn deinit(self: *AllErrors, gpa: *Allocator) void {
- self.arena.promote(gpa).deinit();
- }
-
- fn add(
- arena: *std.heap.ArenaAllocator,
- errors: *std.ArrayList(Message),
- sub_file_path: []const u8,
- source: []const u8,
- simple_err_msg: ErrorMsg,
- ) !void {
- const loc = std.zig.findLineColumn(source, simple_err_msg.byte_offset);
- try errors.append(.{
- .src_path = try arena.allocator.dupe(u8, sub_file_path),
- .msg = try arena.allocator.dupe(u8, simple_err_msg.msg),
- .byte_offset = simple_err_msg.byte_offset,
- .line = loc.line,
- .column = loc.column,
- });
- }
-};
-
-pub const InitOptions = struct {
- target: std.Target,
- root_name: []const u8,
- root_pkg: *Package,
- output_mode: std.builtin.OutputMode,
- bin_file_dir: ?std.fs.Dir = null,
- bin_file_path: []const u8,
- link_mode: ?std.builtin.LinkMode = null,
- object_format: ?std.builtin.ObjectFormat = null,
- optimize_mode: std.builtin.Mode = .Debug,
- keep_source_files_loaded: bool = false,
-};
-
-pub fn init(gpa: *Allocator, options: InitOptions) !Module {
- const root_name = try gpa.dupe(u8, options.root_name);
- errdefer gpa.free(root_name);
-
- const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();
- const bin_file = try link.File.openPath(gpa, bin_file_dir, options.bin_file_path, .{
- .root_name = root_name,
- .root_pkg = options.root_pkg,
- .target = options.target,
- .output_mode = options.output_mode,
- .link_mode = options.link_mode orelse .Static,
- .object_format = options.object_format orelse options.target.getObjectFormat(),
- .optimize_mode = options.optimize_mode,
- });
- errdefer bin_file.destroy();
-
- const root_scope = blk: {
- if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zig")) {
- const root_scope = try gpa.create(Scope.File);
- root_scope.* = .{
- .sub_file_path = options.root_pkg.root_src_path,
- .source = .{ .unloaded = {} },
- .contents = .{ .not_available = {} },
- .status = .never_loaded,
- .root_container = .{
- .file_scope = root_scope,
- .decls = .{},
- },
- };
- break :blk &root_scope.base;
- } else if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zir")) {
- const root_scope = try gpa.create(Scope.ZIRModule);
- root_scope.* = .{
- .sub_file_path = options.root_pkg.root_src_path,
- .source = .{ .unloaded = {} },
- .contents = .{ .not_available = {} },
- .status = .never_loaded,
- .decls = .{},
- };
- break :blk &root_scope.base;
- } else {
- unreachable;
- }
- };
-
- return Module{
- .gpa = gpa,
- .root_name = root_name,
- .root_pkg = options.root_pkg,
- .root_scope = root_scope,
- .bin_file_dir = bin_file_dir,
- .bin_file_path = options.bin_file_path,
- .bin_file = bin_file,
- .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),
- .keep_source_files_loaded = options.keep_source_files_loaded,
- };
-}
+pub const InnerError = error{ OutOfMemory, AnalysisFail };
pub fn deinit(self: *Module) void {
- self.bin_file.destroy();
const gpa = self.gpa;
- self.gpa.free(self.root_name);
+
+ self.zig_cache_artifact_directory.handle.close();
+
self.deletion_set.deinit(gpa);
- self.work_queue.deinit();
for (self.decl_table.items()) |entry| {
entry.value.destroy(gpa);
@@ -969,7 +854,6 @@ pub fn deinit(self: *Module) void {
gpa.free(entry.key);
}
self.global_error_set.deinit(gpa);
- self.* = undefined;
}
fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
@@ -980,204 +864,6 @@ fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
gpa.free(export_list);
}
-pub fn target(self: Module) std.Target {
- return self.bin_file.options.target;
-}
-
-pub fn optimizeMode(self: Module) std.builtin.Mode {
- return self.bin_file.options.optimize_mode;
-}
-
-/// Detect changes to source files, perform semantic analysis, and update the output files.
-pub fn update(self: *Module) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- self.generation += 1;
-
- // TODO Use the cache hash file system to detect which source files changed.
- // Until then we simulate a full cache miss. Source files could have been loaded for any reason;
- // to force a refresh we unload now.
- if (self.root_scope.cast(Scope.File)) |zig_file| {
- zig_file.unload(self.gpa);
- self.analyzeContainer(&zig_file.root_container) catch |err| switch (err) {
- error.AnalysisFail => {
- assert(self.totalErrorCount() != 0);
- },
- else => |e| return e,
- };
- } else if (self.root_scope.cast(Scope.ZIRModule)) |zir_module| {
- zir_module.unload(self.gpa);
- self.analyzeRootZIRModule(zir_module) catch |err| switch (err) {
- error.AnalysisFail => {
- assert(self.totalErrorCount() != 0);
- },
- else => |e| return e,
- };
- }
-
- try self.performAllTheWork();
-
- // Process the deletion set.
- while (self.deletion_set.popOrNull()) |decl| {
- if (decl.dependants.items().len != 0) {
- decl.deletion_flag = false;
- continue;
- }
- try self.deleteDecl(decl);
- }
-
- // This is needed before reading the error flags.
- try self.bin_file.flush(self);
-
- self.link_error_flags = self.bin_file.errorFlags();
-
- // If there are any errors, we anticipate the source files being loaded
- // to report error messages. Otherwise we unload all source files to save memory.
- if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) {
- self.root_scope.unload(self.gpa);
- }
-}
-
-/// Having the file open for writing is problematic as far as executing the
-/// binary is concerned. This will remove the write flag, or close the file,
-/// or whatever is needed so that it can be executed.
-/// After this, one must call` makeFileWritable` before calling `update`.
-pub fn makeBinFileExecutable(self: *Module) !void {
- return self.bin_file.makeExecutable();
-}
-
-pub fn makeBinFileWritable(self: *Module) !void {
- return self.bin_file.makeWritable(self.bin_file_dir, self.bin_file_path);
-}
-
-pub fn totalErrorCount(self: *Module) usize {
- const total = self.failed_decls.items().len +
- self.failed_files.items().len +
- self.failed_exports.items().len;
- return if (total == 0) @boolToInt(self.link_error_flags.no_entry_point_found) else total;
-}
-
-pub fn getAllErrorsAlloc(self: *Module) !AllErrors {
- var arena = std.heap.ArenaAllocator.init(self.gpa);
- errdefer arena.deinit();
-
- var errors = std.ArrayList(AllErrors.Message).init(self.gpa);
- defer errors.deinit();
-
- for (self.failed_files.items()) |entry| {
- const scope = entry.key;
- const err_msg = entry.value;
- const source = try scope.getSource(self);
- try AllErrors.add(&arena, &errors, scope.subFilePath(), source, err_msg.*);
- }
- for (self.failed_decls.items()) |entry| {
- const decl = entry.key;
- const err_msg = entry.value;
- const source = try decl.scope.getSource(self);
- try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
- }
- for (self.failed_exports.items()) |entry| {
- const decl = entry.key.owner_decl;
- const err_msg = entry.value;
- const source = try decl.scope.getSource(self);
- try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
- }
-
- if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) {
- try errors.append(.{
- .src_path = self.root_pkg.root_src_path,
- .line = 0,
- .column = 0,
- .byte_offset = 0,
- .msg = try std.fmt.allocPrint(&arena.allocator, "no entry point found", .{}),
- });
- }
-
- assert(errors.items.len == self.totalErrorCount());
-
- return AllErrors{
- .list = try arena.allocator.dupe(AllErrors.Message, errors.items),
- .arena = arena.state,
- };
-}
-
-pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
- while (self.work_queue.readItem()) |work_item| switch (work_item) {
- .codegen_decl => |decl| switch (decl.analysis) {
- .unreferenced => unreachable,
- .in_progress => unreachable,
- .outdated => unreachable,
-
- .sema_failure,
- .codegen_failure,
- .dependency_failure,
- .sema_failure_retryable,
- => continue,
-
- .complete, .codegen_failure_retryable => {
- if (decl.typed_value.most_recent.typed_value.val.cast(Value.Payload.Function)) |payload| {
- switch (payload.func.analysis) {
- .queued => self.analyzeFnBody(decl, payload.func) catch |err| switch (err) {
- error.AnalysisFail => {
- assert(payload.func.analysis != .in_progress);
- continue;
- },
- error.OutOfMemory => return error.OutOfMemory,
- },
- .in_progress => unreachable,
- .sema_failure, .dependency_failure => continue,
- .success => {},
- }
- // Here we tack on additional allocations to the Decl's arena. The allocations are
- // lifetime annotations in the ZIR.
- var decl_arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);
- defer decl.typed_value.most_recent.arena.?.* = decl_arena.state;
- log.debug("analyze liveness of {}\n", .{decl.name});
- try liveness.analyze(self.gpa, &decl_arena.allocator, payload.func.analysis.success);
- }
-
- assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());
-
- self.bin_file.updateDecl(self, decl) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.AnalysisFail => {
- decl.analysis = .dependency_failure;
- },
- else => {
- try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
- self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
- self.gpa,
- decl.src(),
- "unable to codegen: {}",
- .{@errorName(err)},
- ));
- decl.analysis = .codegen_failure_retryable;
- },
- };
- },
- },
- .analyze_decl => |decl| {
- self.ensureDeclAnalyzed(decl) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.AnalysisFail => continue,
- };
- },
- .update_line_number => |decl| {
- self.bin_file.updateDeclLineNumber(self, decl) catch |err| {
- try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
- self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
- self.gpa,
- decl.src(),
- "unable to update line number: {}",
- .{@errorName(err)},
- ));
- decl.analysis = .codegen_failure_retryable;
- };
- },
- };
-}
-
pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
const tracy = trace(@src());
defer tracy.end();
@@ -1227,7 +913,7 @@ pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
error.AnalysisFail => return error.AnalysisFail,
else => {
try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
- self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
+ self.failed_decls.putAssumeCapacityNoClobber(decl, try Compilation.ErrorMsg.create(
self.gpa,
decl.src(),
"unable to analyze: {}",
@@ -1457,10 +1143,10 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
// We don't fully codegen the decl until later, but we do need to reserve a global
// offset table index for it. This allows us to codegen decls out of dependency order,
// increasing how many computations can be done in parallel.
- try self.bin_file.allocateDeclIndexes(decl);
- try self.work_queue.writeItem(.{ .codegen_decl = decl });
+ try self.comp.bin_file.allocateDeclIndexes(decl);
+ try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });
} else if (prev_type_has_bits) {
- self.bin_file.freeDecl(decl);
+ self.comp.bin_file.freeDecl(decl);
}
if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
@@ -1708,7 +1394,7 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
if (zir_module.error_msg) |src_err_msg| {
self.failed_files.putAssumeCapacityNoClobber(
&root_scope.base,
- try ErrorMsg.create(self.gpa, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}),
+ try Compilation.ErrorMsg.create(self.gpa, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}),
);
root_scope.status = .unloaded_parse_failure;
return error.AnalysisFail;
@@ -1752,7 +1438,7 @@ fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree {
defer msg.deinit();
try parse_err.render(tree.token_ids, msg.outStream());
- const err_msg = try self.gpa.create(ErrorMsg);
+ const err_msg = try self.gpa.create(Compilation.ErrorMsg);
err_msg.* = .{
.msg = msg.toOwnedSlice(),
.byte_offset = tree.token_locs[parse_err.loc()].start,
@@ -1776,7 +1462,7 @@ fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree {
}
}
-fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
+pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
const tracy = trace(@src());
defer tracy.end();
@@ -1785,7 +1471,7 @@ fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
const tree = try self.getAstTree(container_scope);
const decls = tree.root_node.decls();
- try self.work_queue.ensureUnusedCapacity(decls.len);
+ try self.comp.work_queue.ensureUnusedCapacity(decls.len);
try container_scope.decls.ensureCapacity(self.gpa, decls.len);
// Keep track of the decls that we expect to see in this file so that
@@ -1814,21 +1500,21 @@ fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
decl.src_index = decl_i;
if (deleted_decls.remove(decl) == null) {
decl.analysis = .sema_failure;
- const err_msg = try ErrorMsg.create(self.gpa, tree.token_locs[name_tok].start, "redefinition of '{}'", .{decl.name});
+ const err_msg = try Compilation.ErrorMsg.create(self.gpa, tree.token_locs[name_tok].start, "redefinition of '{}'", .{decl.name});
errdefer err_msg.destroy(self.gpa);
try self.failed_decls.putNoClobber(self.gpa, decl, err_msg);
} else {
if (!srcHashEql(decl.contents_hash, contents_hash)) {
try self.markOutdatedDecl(decl);
decl.contents_hash = contents_hash;
- } else switch (self.bin_file.tag) {
+ } else switch (self.comp.bin_file.tag) {
.coff => {
// TODO Implement for COFF
},
.elf => if (decl.fn_link.elf.len != 0) {
// TODO Look into detecting when this would be unnecessary by storing enough state
// in `Decl` to notice that the line number did not change.
- self.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
+ self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
},
.macho => {
// TODO Implement for MachO
@@ -1841,7 +1527,7 @@ fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
container_scope.decls.putAssumeCapacity(new_decl, {});
if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
if (tree.token_ids[maybe_export_token] == .Keyword_export) {
- self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
+ self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
}
}
}
@@ -1856,7 +1542,7 @@ fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
decl.src_index = decl_i;
if (deleted_decls.remove(decl) == null) {
decl.analysis = .sema_failure;
- const err_msg = try ErrorMsg.create(self.gpa, name_loc.start, "redefinition of '{}'", .{decl.name});
+ const err_msg = try Compilation.ErrorMsg.create(self.gpa, name_loc.start, "redefinition of '{}'", .{decl.name});
errdefer err_msg.destroy(self.gpa);
try self.failed_decls.putNoClobber(self.gpa, decl, err_msg);
} else if (!srcHashEql(decl.contents_hash, contents_hash)) {
@@ -1868,7 +1554,7 @@ fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
container_scope.decls.putAssumeCapacity(new_decl, {});
if (var_decl.getExternExportToken()) |maybe_export_token| {
if (tree.token_ids[maybe_export_token] == .Keyword_export) {
- self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
+ self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
}
}
}
@@ -1882,7 +1568,7 @@ fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
container_scope.decls.putAssumeCapacity(new_decl, {});
- self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
+ self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
} else if (src_decl.castTag(.ContainerField)) |container_field| {
log.err("TODO: analyze container field", .{});
} else if (src_decl.castTag(.TestDecl)) |test_decl| {
@@ -1901,12 +1587,12 @@ fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
}
}
-fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
+pub fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
// We may be analyzing it for the first time, or this may be
// an incremental update. This code handles both cases.
const src_module = try self.getSrcModule(root_scope);
- try self.work_queue.ensureUnusedCapacity(src_module.decls.len);
+ try self.comp.work_queue.ensureUnusedCapacity(src_module.decls.len);
try root_scope.decls.ensureCapacity(self.gpa, src_module.decls.len);
var exports_to_resolve = std.ArrayList(*zir.Decl).init(self.gpa);
@@ -1954,7 +1640,7 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
}
}
-fn deleteDecl(self: *Module, decl: *Decl) !void {
+pub fn deleteDecl(self: *Module, decl: *Decl) !void {
try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len);
// Remove from the namespace it resides in. In the case of an anonymous Decl it will
@@ -1988,7 +1674,7 @@ fn deleteDecl(self: *Module, decl: *Decl) !void {
entry.value.destroy(self.gpa);
}
self.deleteDeclExports(decl);
- self.bin_file.freeDecl(decl);
+ self.comp.bin_file.freeDecl(decl);
decl.destroy(self.gpa);
}
@@ -2016,7 +1702,7 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {
self.decl_exports.removeAssertDiscard(exp.exported_decl);
}
}
- if (self.bin_file.cast(link.File.Elf)) |elf| {
+ if (self.comp.bin_file.cast(link.File.Elf)) |elf| {
elf.deleteExport(exp.link);
}
if (self.failed_exports.remove(exp)) |entry| {
@@ -2029,7 +1715,7 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {
self.gpa.free(kv.value);
}
-fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
+pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
const tracy = trace(@src());
defer tracy.end();
@@ -2060,7 +1746,7 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
log.debug("mark {} outdated\n", .{decl.name});
- try self.work_queue.writeItem(.{ .analyze_decl = decl });
+ try self.comp.work_queue.writeItem(.{ .analyze_decl = decl });
if (self.failed_decls.remove(decl)) |entry| {
entry.value.destroy(self.gpa);
}
@@ -2082,14 +1768,14 @@ fn allocateNewDecl(
.analysis = .unreferenced,
.deletion_flag = false,
.contents_hash = contents_hash,
- .link = switch (self.bin_file.tag) {
+ .link = switch (self.comp.bin_file.tag) {
.coff => .{ .coff = link.File.Coff.TextBlock.empty },
.elf => .{ .elf = link.File.Elf.TextBlock.empty },
.macho => .{ .macho = link.File.MachO.TextBlock.empty },
.c => .{ .c = {} },
.wasm => .{ .wasm = {} },
},
- .fn_link = switch (self.bin_file.tag) {
+ .fn_link = switch (self.comp.bin_file.tag) {
.coff => .{ .coff = {} },
.elf => .{ .elf = link.File.Elf.SrcFn.empty },
.macho => .{ .macho = link.File.MachO.SrcFn.empty },
@@ -2206,7 +1892,7 @@ pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_n
if (self.symbol_exports.get(symbol_name)) |_| {
try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1);
- self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(
+ self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create(
self.gpa,
src,
"exported symbol collision: {}",
@@ -2218,11 +1904,11 @@ pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_n
}
try self.symbol_exports.putNoClobber(self.gpa, symbol_name, new_export);
- self.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) {
+ self.comp.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => {
try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1);
- self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(
+ self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create(
self.gpa,
src,
"unable to export: {}",
@@ -2502,8 +2188,8 @@ pub fn createAnonymousDecl(
// We should be able to further improve the compiler to not omit Decls which are only referenced at
// compile-time and not runtime.
if (typed_value.ty.hasCodeGenBits()) {
- try self.bin_file.allocateDeclIndexes(new_decl);
- try self.work_queue.writeItem(.{ .codegen_decl = new_decl });
+ try self.comp.bin_file.allocateDeclIndexes(new_decl);
+ try self.comp.work_queue.writeItem(.{ .codegen_decl = new_decl });
}
return new_decl;
@@ -2756,7 +2442,7 @@ pub fn cmpNumeric(
} else if (rhs_ty_tag == .ComptimeFloat) {
break :x lhs.ty;
}
- if (lhs.ty.floatBits(self.target()) >= rhs.ty.floatBits(self.target())) {
+ if (lhs.ty.floatBits(self.getTarget()) >= rhs.ty.floatBits(self.getTarget())) {
break :x lhs.ty;
} else {
break :x rhs.ty;
@@ -2815,7 +2501,7 @@ pub fn cmpNumeric(
} else if (lhs_is_float) {
dest_float_type = lhs.ty;
} else {
- const int_info = lhs.ty.intInfo(self.target());
+ const int_info = lhs.ty.intInfo(self.getTarget());
lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
}
@@ -2850,7 +2536,7 @@ pub fn cmpNumeric(
} else if (rhs_is_float) {
dest_float_type = rhs.ty;
} else {
- const int_info = rhs.ty.intInfo(self.target());
+ const int_info = rhs.ty.intInfo(self.getTarget());
rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
}
@@ -2915,13 +2601,13 @@ pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Ty
next_inst.ty.isInt() and
prev_inst.ty.isSignedInt() == next_inst.ty.isSignedInt())
{
- if (prev_inst.ty.intInfo(self.target()).bits < next_inst.ty.intInfo(self.target()).bits) {
+ if (prev_inst.ty.intInfo(self.getTarget()).bits < next_inst.ty.intInfo(self.getTarget()).bits) {
prev_inst = next_inst;
}
continue;
}
if (prev_inst.ty.isFloat() and next_inst.ty.isFloat()) {
- if (prev_inst.ty.floatBits(self.target()) < next_inst.ty.floatBits(self.target())) {
+ if (prev_inst.ty.floatBits(self.getTarget()) < next_inst.ty.floatBits(self.getTarget())) {
prev_inst = next_inst;
}
continue;
@@ -2989,10 +2675,10 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) {
assert(inst.value() == null); // handled above
- const src_info = inst.ty.intInfo(self.target());
- const dst_info = dest_type.intInfo(self.target());
+ const src_info = inst.ty.intInfo(self.getTarget());
+ const dst_info = dest_type.intInfo(self.getTarget());
if ((src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) or
- // small enough unsigned ints can get casted to large enough signed ints
+ // small enough unsigned ints can get casted to large enough signed ints
(src_info.signed and !dst_info.signed and dst_info.bits > src_info.bits))
{
const b = try self.requireRuntimeBlock(scope, inst.src);
@@ -3004,8 +2690,8 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
if (inst.ty.zigTypeTag() == .Float and dest_type.zigTypeTag() == .Float) {
assert(inst.value() == null); // handled above
- const src_bits = inst.ty.floatBits(self.target());
- const dst_bits = dest_type.floatBits(self.target());
+ const src_bits = inst.ty.floatBits(self.getTarget());
+ const dst_bits = dest_type.floatBits(self.getTarget());
if (dst_bits >= src_bits) {
const b = try self.requireRuntimeBlock(scope, inst.src);
return self.addUnOp(b, inst.src, dest_type, .floatcast, inst);
@@ -3027,14 +2713,14 @@ pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !?*
}
return self.fail(scope, inst.src, "TODO float to int", .{});
} else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
- if (!val.intFitsInType(dest_type, self.target())) {
+ if (!val.intFitsInType(dest_type, self.getTarget())) {
return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
}
return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
}
} else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {
if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
- const res = val.floatCast(scope.arena(), dest_type, self.target()) catch |err| switch (err) {
+ const res = val.floatCast(scope.arena(), dest_type, self.getTarget()) catch |err| switch (err) {
error.Overflow => return self.fail(
scope,
inst.src,
@@ -3087,7 +2773,7 @@ fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *I
pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError {
@setCold(true);
- const err_msg = try ErrorMsg.create(self.gpa, src, format, args);
+ const err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args);
return self.failWithOwnedErrorMsg(scope, src, err_msg);
}
@@ -3115,7 +2801,7 @@ pub fn failNode(
return self.fail(scope, src, format, args);
}
-fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *ErrorMsg) InnerError {
+fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Compilation.ErrorMsg) InnerError {
{
errdefer err_msg.destroy(self.gpa);
try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
@@ -3181,36 +2867,6 @@ fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult
return .no_match;
}
-pub const ErrorMsg = struct {
- byte_offset: usize,
- msg: []const u8,
-
- pub fn create(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !*ErrorMsg {
- const self = try gpa.create(ErrorMsg);
- errdefer gpa.destroy(self);
- self.* = try init(gpa, byte_offset, format, args);
- return self;
- }
-
- /// Assumes the ErrorMsg struct and msg were both allocated with allocator.
- pub fn destroy(self: *ErrorMsg, gpa: *Allocator) void {
- self.deinit(gpa);
- gpa.destroy(self);
- }
-
- pub fn init(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !ErrorMsg {
- return ErrorMsg{
- .byte_offset = byte_offset,
- .msg = try std.fmt.allocPrint(gpa, format, args),
- };
- }
-
- pub fn deinit(self: *ErrorMsg, gpa: *Allocator) void {
- gpa.free(self.msg);
- self.* = undefined;
- }
-};
-
fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool {
return @bitCast(u128, a) == @bitCast(u128, b);
}
@@ -3274,7 +2930,7 @@ pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
pub fn floatAdd(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value {
var bit_count = switch (float_type.tag()) {
.comptime_float => 128,
- else => float_type.floatBits(self.target()),
+ else => float_type.floatBits(self.getTarget()),
};
const allocator = scope.arena();
@@ -3308,7 +2964,7 @@ pub fn floatAdd(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs:
pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value {
var bit_count = switch (float_type.tag()) {
.comptime_float => 128,
- else => float_type.floatBits(self.target()),
+ else => float_type.floatBits(self.getTarget()),
};
const allocator = scope.arena();
@@ -3579,3 +3235,11 @@ pub fn safetyPanic(mod: *Module, block: *Scope.Block, src: usize, panic_id: Pani
_ = try mod.addNoOp(block, src, Type.initTag(.void), .breakpoint);
return mod.addNoOp(block, src, Type.initTag(.noreturn), .unreach);
}
+
+pub fn getTarget(self: Module) Target {
+ return self.comp.bin_file.options.target;
+}
+
+pub fn optimizeMode(self: Module) std.builtin.Mode {
+ return self.comp.bin_file.options.optimize_mode;
+}
diff --git a/src/Package.zig b/src/Package.zig
new file mode 100644
index 0000000000..8a0e89f883
--- /dev/null
+++ b/src/Package.zig
@@ -0,0 +1,62 @@
+pub const Table = std.StringHashMapUnmanaged(*Package);
+
+root_src_directory: Compilation.Directory,
+/// Relative to `root_src_directory`. May contain path separators.
+root_src_path: []const u8,
+table: Table = .{},
+parent: ?*Package = null,
+
+const std = @import("std");
+const mem = std.mem;
+const Allocator = std.mem.Allocator;
+const assert = std.debug.assert;
+const Package = @This();
+const Compilation = @import("Compilation.zig");
+
+/// No references to `root_src_dir` and `root_src_path` are kept.
+pub fn create(
+ gpa: *Allocator,
+ base_directory: Compilation.Directory,
+ /// Relative to `base_directory`.
+ root_src_dir: []const u8,
+ /// Relative to `root_src_dir`.
+ root_src_path: []const u8,
+) !*Package {
+ const ptr = try gpa.create(Package);
+ errdefer gpa.destroy(ptr);
+
+ const root_src_dir_path = try base_directory.join(gpa, &[_][]const u8{root_src_dir});
+ errdefer gpa.free(root_src_dir_path);
+
+ const root_src_path_dupe = try mem.dupe(gpa, u8, root_src_path);
+ errdefer gpa.free(root_src_path_dupe);
+
+ ptr.* = .{
+ .root_src_directory = .{
+ .path = root_src_dir_path,
+ .handle = try base_directory.handle.openDir(root_src_dir, .{}),
+ },
+ .root_src_path = root_src_path_dupe,
+ };
+ return ptr;
+}
+
+pub fn destroy(pkg: *Package, gpa: *Allocator) void {
+ pkg.root_src_directory.handle.close();
+ gpa.free(pkg.root_src_path);
+ if (pkg.root_src_directory.path) |p| gpa.free(p);
+ {
+ var it = pkg.table.iterator();
+ while (it.next()) |kv| {
+ gpa.free(kv.key);
+ }
+ }
+ pkg.table.deinit(gpa);
+ gpa.destroy(pkg);
+}
+
+pub fn add(pkg: *Package, gpa: *Allocator, name: []const u8, package: *Package) !void {
+ try pkg.table.ensureCapacity(gpa, pkg.table.items().len + 1);
+ const name_dupe = try mem.dupe(gpa, u8, name);
+ pkg.table.putAssumeCapacityNoClobber(name_dupe, package);
+}
diff --git a/src-self-hosted/TypedValue.zig b/src/TypedValue.zig
similarity index 100%
rename from src-self-hosted/TypedValue.zig
rename to src/TypedValue.zig
diff --git a/src-self-hosted/astgen.zig b/src/astgen.zig
similarity index 100%
rename from src-self-hosted/astgen.zig
rename to src/astgen.zig
diff --git a/src/blake2.h b/src/blake2.h
deleted file mode 100644
index 6420c5367a..0000000000
--- a/src/blake2.h
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- BLAKE2 reference source code package - reference C implementations
-
- Copyright 2012, Samuel Neves . You may use this under the
- terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
- your option. The terms of these licenses can be found at:
-
- - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
- - OpenSSL license : https://www.openssl.org/source/license.html
- - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
-
- More information about the BLAKE2 hash function can be found at
- https://blake2.net.
-*/
-#ifndef BLAKE2_H
-#define BLAKE2_H
-
-#include
-#include
-
-#if defined(_MSC_VER)
-#define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop))
-#else
-#define BLAKE2_PACKED(x) x __attribute__((packed))
-#endif
-
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
- enum blake2s_constant
- {
- BLAKE2S_BLOCKBYTES = 64,
- BLAKE2S_OUTBYTES = 32,
- BLAKE2S_KEYBYTES = 32,
- BLAKE2S_SALTBYTES = 8,
- BLAKE2S_PERSONALBYTES = 8
- };
-
- enum blake2b_constant
- {
- BLAKE2B_BLOCKBYTES = 128,
- BLAKE2B_OUTBYTES = 64,
- BLAKE2B_KEYBYTES = 64,
- BLAKE2B_SALTBYTES = 16,
- BLAKE2B_PERSONALBYTES = 16
- };
-
- typedef struct blake2s_state__
- {
- uint32_t h[8];
- uint32_t t[2];
- uint32_t f[2];
- uint8_t buf[BLAKE2S_BLOCKBYTES];
- size_t buflen;
- size_t outlen;
- uint8_t last_node;
- } blake2s_state;
-
- typedef struct blake2b_state__
- {
- uint64_t h[8];
- uint64_t t[2];
- uint64_t f[2];
- uint8_t buf[BLAKE2B_BLOCKBYTES];
- size_t buflen;
- size_t outlen;
- uint8_t last_node;
- } blake2b_state;
-
- typedef struct blake2sp_state__
- {
- blake2s_state S[8][1];
- blake2s_state R[1];
- uint8_t buf[8 * BLAKE2S_BLOCKBYTES];
- size_t buflen;
- size_t outlen;
- } blake2sp_state;
-
- typedef struct blake2bp_state__
- {
- blake2b_state S[4][1];
- blake2b_state R[1];
- uint8_t buf[4 * BLAKE2B_BLOCKBYTES];
- size_t buflen;
- size_t outlen;
- } blake2bp_state;
-
-
- BLAKE2_PACKED(struct blake2s_param__
- {
- uint8_t digest_length; /* 1 */
- uint8_t key_length; /* 2 */
- uint8_t fanout; /* 3 */
- uint8_t depth; /* 4 */
- uint32_t leaf_length; /* 8 */
- uint32_t node_offset; /* 12 */
- uint16_t xof_length; /* 14 */
- uint8_t node_depth; /* 15 */
- uint8_t inner_length; /* 16 */
- /* uint8_t reserved[0]; */
- uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */
- uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */
- });
-
- typedef struct blake2s_param__ blake2s_param;
-
- BLAKE2_PACKED(struct blake2b_param__
- {
- uint8_t digest_length; /* 1 */
- uint8_t key_length; /* 2 */
- uint8_t fanout; /* 3 */
- uint8_t depth; /* 4 */
- uint32_t leaf_length; /* 8 */
- uint32_t node_offset; /* 12 */
- uint32_t xof_length; /* 16 */
- uint8_t node_depth; /* 17 */
- uint8_t inner_length; /* 18 */
- uint8_t reserved[14]; /* 32 */
- uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */
- uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */
- });
-
- typedef struct blake2b_param__ blake2b_param;
-
- typedef struct blake2xs_state__
- {
- blake2s_state S[1];
- blake2s_param P[1];
- } blake2xs_state;
-
- typedef struct blake2xb_state__
- {
- blake2b_state S[1];
- blake2b_param P[1];
- } blake2xb_state;
-
- /* Padded structs result in a compile-time error */
- enum {
- BLAKE2_DUMMY_1 = 1/(sizeof(blake2s_param) == BLAKE2S_OUTBYTES),
- BLAKE2_DUMMY_2 = 1/(sizeof(blake2b_param) == BLAKE2B_OUTBYTES)
- };
-
- /* Streaming API */
- int blake2s_init( blake2s_state *S, size_t outlen );
- int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
- int blake2s_init_param( blake2s_state *S, const blake2s_param *P );
- int blake2s_update( blake2s_state *S, const void *in, size_t inlen );
- int blake2s_final( blake2s_state *S, void *out, size_t outlen );
-
- int blake2b_init( blake2b_state *S, size_t outlen );
- int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
- int blake2b_init_param( blake2b_state *S, const blake2b_param *P );
- int blake2b_update( blake2b_state *S, const void *in, size_t inlen );
- int blake2b_final( blake2b_state *S, void *out, size_t outlen );
-
- int blake2sp_init( blake2sp_state *S, size_t outlen );
- int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen );
- int blake2sp_update( blake2sp_state *S, const void *in, size_t inlen );
- int blake2sp_final( blake2sp_state *S, void *out, size_t outlen );
-
- int blake2bp_init( blake2bp_state *S, size_t outlen );
- int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen );
- int blake2bp_update( blake2bp_state *S, const void *in, size_t inlen );
- int blake2bp_final( blake2bp_state *S, void *out, size_t outlen );
-
- /* Variable output length API */
- int blake2xs_init( blake2xs_state *S, const size_t outlen );
- int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen );
- int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen );
- int blake2xs_final(blake2xs_state *S, void *out, size_t outlen);
-
- int blake2xb_init( blake2xb_state *S, const size_t outlen );
- int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen );
- int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen );
- int blake2xb_final(blake2xb_state *S, void *out, size_t outlen);
-
- /* Simple API */
- int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
- int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
-
- int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
- int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
-
- int blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
- int blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
-
- /* This is simply an alias for blake2b */
- int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
-
-#if defined(__cplusplus)
-}
-#endif
-
-#endif
-
diff --git a/src/blake2b.c b/src/blake2b.c
deleted file mode 100644
index 600f951b9b..0000000000
--- a/src/blake2b.c
+++ /dev/null
@@ -1,539 +0,0 @@
-/*
- BLAKE2 reference source code package - reference C implementations
-
- Copyright 2012, Samuel Neves . You may use this under the
- terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
- your option. The terms of these licenses can be found at:
-
- - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
- - OpenSSL license : https://www.openssl.org/source/license.html
- - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
-
- More information about the BLAKE2 hash function can be found at
- https://blake2.net.
-*/
-
-#include
-#include
-#include
-
-#include "blake2.h"
-/*
- BLAKE2 reference source code package - reference C implementations
-
- Copyright 2012, Samuel Neves . You may use this under the
- terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
- your option. The terms of these licenses can be found at:
-
- - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
- - OpenSSL license : https://www.openssl.org/source/license.html
- - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
-
- More information about the BLAKE2 hash function can be found at
- https://blake2.net.
-*/
-#ifndef BLAKE2_IMPL_H
-#define BLAKE2_IMPL_H
-
-#include
-#include
-
-#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L)
- #if defined(_MSC_VER)
- #define BLAKE2_INLINE __inline
- #elif defined(__GNUC__)
- #define BLAKE2_INLINE __inline__
- #else
- #define BLAKE2_INLINE
- #endif
-#else
- #define BLAKE2_INLINE inline
-#endif
-
-static BLAKE2_INLINE uint32_t load32( const void *src )
-{
-#if defined(NATIVE_LITTLE_ENDIAN)
- uint32_t w;
- memcpy(&w, src, sizeof w);
- return w;
-#else
- const uint8_t *p = ( const uint8_t * )src;
- return (( uint32_t )( p[0] ) << 0) |
- (( uint32_t )( p[1] ) << 8) |
- (( uint32_t )( p[2] ) << 16) |
- (( uint32_t )( p[3] ) << 24) ;
-#endif
-}
-
-static BLAKE2_INLINE uint64_t load64( const void *src )
-{
-#if defined(NATIVE_LITTLE_ENDIAN)
- uint64_t w;
- memcpy(&w, src, sizeof w);
- return w;
-#else
- const uint8_t *p = ( const uint8_t * )src;
- return (( uint64_t )( p[0] ) << 0) |
- (( uint64_t )( p[1] ) << 8) |
- (( uint64_t )( p[2] ) << 16) |
- (( uint64_t )( p[3] ) << 24) |
- (( uint64_t )( p[4] ) << 32) |
- (( uint64_t )( p[5] ) << 40) |
- (( uint64_t )( p[6] ) << 48) |
- (( uint64_t )( p[7] ) << 56) ;
-#endif
-}
-
-static BLAKE2_INLINE uint16_t load16( const void *src )
-{
-#if defined(NATIVE_LITTLE_ENDIAN)
- uint16_t w;
- memcpy(&w, src, sizeof w);
- return w;
-#else
- const uint8_t *p = ( const uint8_t * )src;
- return ( uint16_t )((( uint32_t )( p[0] ) << 0) |
- (( uint32_t )( p[1] ) << 8));
-#endif
-}
-
-static BLAKE2_INLINE void store16( void *dst, uint16_t w )
-{
-#if defined(NATIVE_LITTLE_ENDIAN)
- memcpy(dst, &w, sizeof w);
-#else
- uint8_t *p = ( uint8_t * )dst;
- *p++ = ( uint8_t )w; w >>= 8;
- *p++ = ( uint8_t )w;
-#endif
-}
-
-static BLAKE2_INLINE void store32( void *dst, uint32_t w )
-{
-#if defined(NATIVE_LITTLE_ENDIAN)
- memcpy(dst, &w, sizeof w);
-#else
- uint8_t *p = ( uint8_t * )dst;
- p[0] = (uint8_t)(w >> 0);
- p[1] = (uint8_t)(w >> 8);
- p[2] = (uint8_t)(w >> 16);
- p[3] = (uint8_t)(w >> 24);
-#endif
-}
-
-static BLAKE2_INLINE void store64( void *dst, uint64_t w )
-{
-#if defined(NATIVE_LITTLE_ENDIAN)
- memcpy(dst, &w, sizeof w);
-#else
- uint8_t *p = ( uint8_t * )dst;
- p[0] = (uint8_t)(w >> 0);
- p[1] = (uint8_t)(w >> 8);
- p[2] = (uint8_t)(w >> 16);
- p[3] = (uint8_t)(w >> 24);
- p[4] = (uint8_t)(w >> 32);
- p[5] = (uint8_t)(w >> 40);
- p[6] = (uint8_t)(w >> 48);
- p[7] = (uint8_t)(w >> 56);
-#endif
-}
-
-static BLAKE2_INLINE uint64_t load48( const void *src )
-{
- const uint8_t *p = ( const uint8_t * )src;
- return (( uint64_t )( p[0] ) << 0) |
- (( uint64_t )( p[1] ) << 8) |
- (( uint64_t )( p[2] ) << 16) |
- (( uint64_t )( p[3] ) << 24) |
- (( uint64_t )( p[4] ) << 32) |
- (( uint64_t )( p[5] ) << 40) ;
-}
-
-static BLAKE2_INLINE void store48( void *dst, uint64_t w )
-{
- uint8_t *p = ( uint8_t * )dst;
- p[0] = (uint8_t)(w >> 0);
- p[1] = (uint8_t)(w >> 8);
- p[2] = (uint8_t)(w >> 16);
- p[3] = (uint8_t)(w >> 24);
- p[4] = (uint8_t)(w >> 32);
- p[5] = (uint8_t)(w >> 40);
-}
-
-static BLAKE2_INLINE uint32_t rotr32( const uint32_t w, const unsigned c )
-{
- return ( w >> c ) | ( w << ( 32 - c ) );
-}
-
-static BLAKE2_INLINE uint64_t rotr64( const uint64_t w, const unsigned c )
-{
- return ( w >> c ) | ( w << ( 64 - c ) );
-}
-
-/* prevents compiler optimizing out memset() */
-static BLAKE2_INLINE void secure_zero_memory(void *v, size_t n)
-{
- static void *(*const volatile memset_v)(void *, int, size_t) = &memset;
- memset_v(v, 0, n);
-}
-
-#endif
-
-static const uint64_t blake2b_IV[8] =
-{
- 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL,
- 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL,
- 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL,
- 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
-};
-
-static const uint8_t blake2b_sigma[12][16] =
-{
- { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
- { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
- { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
- { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
- { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
- { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
- { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
- { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
- { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
- { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
- { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
- { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }
-};
-
-
-static void blake2b_set_lastnode( blake2b_state *S )
-{
- S->f[1] = (uint64_t)-1;
-}
-
-/* Some helper functions, not necessarily useful */
-static int blake2b_is_lastblock( const blake2b_state *S )
-{
- return S->f[0] != 0;
-}
-
-static void blake2b_set_lastblock( blake2b_state *S )
-{
- if( S->last_node ) blake2b_set_lastnode( S );
-
- S->f[0] = (uint64_t)-1;
-}
-
-static void blake2b_increment_counter( blake2b_state *S, const uint64_t inc )
-{
- S->t[0] += inc;
- S->t[1] += ( S->t[0] < inc );
-}
-
-static void blake2b_init0( blake2b_state *S )
-{
- size_t i;
- memset( S, 0, sizeof( blake2b_state ) );
-
- for( i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i];
-}
-
-/* init xors IV with input parameter block */
-int blake2b_init_param( blake2b_state *S, const blake2b_param *P )
-{
- const uint8_t *p = ( const uint8_t * )( P );
- size_t i;
-
- blake2b_init0( S );
-
- /* IV XOR ParamBlock */
- for( i = 0; i < 8; ++i )
- S->h[i] ^= load64( p + sizeof( S->h[i] ) * i );
-
- S->outlen = P->digest_length;
- return 0;
-}
-
-
-
-int blake2b_init( blake2b_state *S, size_t outlen )
-{
- blake2b_param P[1];
-
- if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1;
-
- P->digest_length = (uint8_t)outlen;
- P->key_length = 0;
- P->fanout = 1;
- P->depth = 1;
- store32( &P->leaf_length, 0 );
- store32( &P->node_offset, 0 );
- store32( &P->xof_length, 0 );
- P->node_depth = 0;
- P->inner_length = 0;
- memset( P->reserved, 0, sizeof( P->reserved ) );
- memset( P->salt, 0, sizeof( P->salt ) );
- memset( P->personal, 0, sizeof( P->personal ) );
- return blake2b_init_param( S, P );
-}
-
-
-int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen )
-{
- blake2b_param P[1];
-
- if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1;
-
- if ( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1;
-
- P->digest_length = (uint8_t)outlen;
- P->key_length = (uint8_t)keylen;
- P->fanout = 1;
- P->depth = 1;
- store32( &P->leaf_length, 0 );
- store32( &P->node_offset, 0 );
- store32( &P->xof_length, 0 );
- P->node_depth = 0;
- P->inner_length = 0;
- memset( P->reserved, 0, sizeof( P->reserved ) );
- memset( P->salt, 0, sizeof( P->salt ) );
- memset( P->personal, 0, sizeof( P->personal ) );
-
- if( blake2b_init_param( S, P ) < 0 ) return -1;
-
- {
- uint8_t block[BLAKE2B_BLOCKBYTES];
- memset( block, 0, BLAKE2B_BLOCKBYTES );
- memcpy( block, key, keylen );
- blake2b_update( S, block, BLAKE2B_BLOCKBYTES );
- secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
- }
- return 0;
-}
-
-#define G(r,i,a,b,c,d) \
- do { \
- a = a + b + m[blake2b_sigma[r][2*i+0]]; \
- d = rotr64(d ^ a, 32); \
- c = c + d; \
- b = rotr64(b ^ c, 24); \
- a = a + b + m[blake2b_sigma[r][2*i+1]]; \
- d = rotr64(d ^ a, 16); \
- c = c + d; \
- b = rotr64(b ^ c, 63); \
- } while(0)
-
-#define ROUND(r) \
- do { \
- G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \
- G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \
- G(r,2,v[ 2],v[ 6],v[10],v[14]); \
- G(r,3,v[ 3],v[ 7],v[11],v[15]); \
- G(r,4,v[ 0],v[ 5],v[10],v[15]); \
- G(r,5,v[ 1],v[ 6],v[11],v[12]); \
- G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \
- G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \
- } while(0)
-
-static void blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] )
-{
- uint64_t m[16];
- uint64_t v[16];
- size_t i;
-
- for( i = 0; i < 16; ++i ) {
- m[i] = load64( block + i * sizeof( m[i] ) );
- }
-
- for( i = 0; i < 8; ++i ) {
- v[i] = S->h[i];
- }
-
- v[ 8] = blake2b_IV[0];
- v[ 9] = blake2b_IV[1];
- v[10] = blake2b_IV[2];
- v[11] = blake2b_IV[3];
- v[12] = blake2b_IV[4] ^ S->t[0];
- v[13] = blake2b_IV[5] ^ S->t[1];
- v[14] = blake2b_IV[6] ^ S->f[0];
- v[15] = blake2b_IV[7] ^ S->f[1];
-
- ROUND( 0 );
- ROUND( 1 );
- ROUND( 2 );
- ROUND( 3 );
- ROUND( 4 );
- ROUND( 5 );
- ROUND( 6 );
- ROUND( 7 );
- ROUND( 8 );
- ROUND( 9 );
- ROUND( 10 );
- ROUND( 11 );
-
- for( i = 0; i < 8; ++i ) {
- S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
- }
-}
-
-#undef G
-#undef ROUND
-
-int blake2b_update( blake2b_state *S, const void *pin, size_t inlen )
-{
- const unsigned char * in = (const unsigned char *)pin;
- if( inlen > 0 )
- {
- size_t left = S->buflen;
- size_t fill = BLAKE2B_BLOCKBYTES - left;
- if( inlen > fill )
- {
- S->buflen = 0;
- memcpy( S->buf + left, in, fill ); /* Fill buffer */
- blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES );
- blake2b_compress( S, S->buf ); /* Compress */
- in += fill; inlen -= fill;
- while(inlen > BLAKE2B_BLOCKBYTES) {
- blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES);
- blake2b_compress( S, in );
- in += BLAKE2B_BLOCKBYTES;
- inlen -= BLAKE2B_BLOCKBYTES;
- }
- }
- memcpy( S->buf + S->buflen, in, inlen );
- S->buflen += inlen;
- }
- return 0;
-}
-
-int blake2b_final( blake2b_state *S, void *out, size_t outlen )
-{
- uint8_t buffer[BLAKE2B_OUTBYTES] = {0};
- size_t i;
-
- if( out == NULL || outlen < S->outlen )
- return -1;
-
- if( blake2b_is_lastblock( S ) )
- return -1;
-
- blake2b_increment_counter( S, S->buflen );
- blake2b_set_lastblock( S );
- memset( S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */
- blake2b_compress( S, S->buf );
-
- for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */
- store64( buffer + sizeof( S->h[i] ) * i, S->h[i] );
-
- memcpy( out, buffer, S->outlen );
- secure_zero_memory(buffer, sizeof(buffer));
- return 0;
-}
-
-/* inlen, at least, should be uint64_t. Others can be size_t. */
-int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
-{
- blake2b_state S[1];
-
- /* Verify parameters */
- if ( NULL == in && inlen > 0 ) return -1;
-
- if ( NULL == out ) return -1;
-
- if( NULL == key && keylen > 0 ) return -1;
-
- if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1;
-
- if( keylen > BLAKE2B_KEYBYTES ) return -1;
-
- if( keylen > 0 )
- {
- if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1;
- }
- else
- {
- if( blake2b_init( S, outlen ) < 0 ) return -1;
- }
-
- blake2b_update( S, ( const uint8_t * )in, inlen );
- blake2b_final( S, out, outlen );
- return 0;
-}
-
-int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) {
- return blake2b(out, outlen, in, inlen, key, keylen);
-}
-
-#if defined(SUPERCOP)
-int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen )
-{
- return blake2b( out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0 );
-}
-#endif
-
-#if defined(BLAKE2B_SELFTEST)
-#include
-#include "blake2-kat.h"
-int main( void )
-{
- uint8_t key[BLAKE2B_KEYBYTES];
- uint8_t buf[BLAKE2_KAT_LENGTH];
- size_t i, step;
-
- for( i = 0; i < BLAKE2B_KEYBYTES; ++i )
- key[i] = ( uint8_t )i;
-
- for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
- buf[i] = ( uint8_t )i;
-
- /* Test simple API */
- for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
- {
- uint8_t hash[BLAKE2B_OUTBYTES];
- blake2b( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES );
-
- if( 0 != memcmp( hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES ) )
- {
- goto fail;
- }
- }
-
- /* Test streaming API */
- for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) {
- for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) {
- uint8_t hash[BLAKE2B_OUTBYTES];
- blake2b_state S;
- uint8_t * p = buf;
- size_t mlen = i;
- int err = 0;
-
- if( (err = blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) {
- goto fail;
- }
-
- while (mlen >= step) {
- if ( (err = blake2b_update(&S, p, step)) < 0 ) {
- goto fail;
- }
- mlen -= step;
- p += step;
- }
- if ( (err = blake2b_update(&S, p, mlen)) < 0) {
- goto fail;
- }
- if ( (err = blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) {
- goto fail;
- }
-
- if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) {
- goto fail;
- }
- }
- }
-
- puts( "ok" );
- return 0;
-fail:
- puts("error");
- return -1;
-}
-#endif
-
diff --git a/src/cache_hash.cpp b/src/cache_hash.cpp
deleted file mode 100644
index c12d8f29ef..0000000000
--- a/src/cache_hash.cpp
+++ /dev/null
@@ -1,595 +0,0 @@
-/*
- * Copyright (c) 2018 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#include "stage2.h"
-#include "cache_hash.hpp"
-#include "all_types.hpp"
-#include "buffer.hpp"
-#include "os.hpp"
-
-#include
-
-void cache_init(CacheHash *ch, Buf *manifest_dir) {
- int rc = blake2b_init(&ch->blake, 48);
- assert(rc == 0);
- ch->files = {};
- ch->manifest_dir = manifest_dir;
- ch->manifest_file_path = nullptr;
- ch->manifest_dirty = false;
- ch->force_check_manifest = false;
- ch->b64_digest = BUF_INIT;
-}
-
-void cache_mem(CacheHash *ch, const char *ptr, size_t len) {
- assert(ch->manifest_file_path == nullptr);
- assert(ptr != nullptr);
- blake2b_update(&ch->blake, ptr, len);
-}
-
-void cache_slice(CacheHash *ch, Slice slice) {
- // mix the length into the hash so that two juxtaposed cached slices can't collide
- cache_usize(ch, slice.len);
- cache_mem(ch, slice.ptr, slice.len);
-}
-
-void cache_str(CacheHash *ch, const char *ptr) {
- // + 1 to include the null byte
- cache_mem(ch, ptr, strlen(ptr) + 1);
-}
-
-void cache_int(CacheHash *ch, int x) {
- assert(ch->manifest_file_path == nullptr);
- // + 1 to include the null byte
- uint8_t buf[sizeof(int) + 1];
- memcpy(buf, &x, sizeof(int));
- buf[sizeof(int)] = 0;
- blake2b_update(&ch->blake, buf, sizeof(int) + 1);
-}
-
-void cache_usize(CacheHash *ch, size_t x) {
- assert(ch->manifest_file_path == nullptr);
- // + 1 to include the null byte
- uint8_t buf[sizeof(size_t) + 1];
- memcpy(buf, &x, sizeof(size_t));
- buf[sizeof(size_t)] = 0;
- blake2b_update(&ch->blake, buf, sizeof(size_t) + 1);
-}
-
-void cache_bool(CacheHash *ch, bool x) {
- assert(ch->manifest_file_path == nullptr);
- blake2b_update(&ch->blake, &x, 1);
-}
-
-void cache_buf(CacheHash *ch, Buf *buf) {
- assert(ch->manifest_file_path == nullptr);
- assert(buf != nullptr);
- // + 1 to include the null byte
- blake2b_update(&ch->blake, buf_ptr(buf), buf_len(buf) + 1);
-}
-
-void cache_buf_opt(CacheHash *ch, Buf *buf) {
- assert(ch->manifest_file_path == nullptr);
- if (buf == nullptr) {
- cache_str(ch, "");
- cache_str(ch, "");
- } else {
- cache_buf(ch, buf);
- }
-}
-
-void cache_list_of_link_lib(CacheHash *ch, LinkLib **ptr, size_t len) {
- assert(ch->manifest_file_path == nullptr);
- for (size_t i = 0; i < len; i += 1) {
- LinkLib *lib = ptr[i];
- if (lib->provided_explicitly) {
- cache_buf(ch, lib->name);
- }
- }
- cache_str(ch, "");
-}
-
-void cache_list_of_buf(CacheHash *ch, Buf **ptr, size_t len) {
- assert(ch->manifest_file_path == nullptr);
- for (size_t i = 0; i < len; i += 1) {
- Buf *buf = ptr[i];
- cache_buf(ch, buf);
- }
- cache_str(ch, "");
-}
-
-void cache_list_of_file(CacheHash *ch, Buf **ptr, size_t len) {
- assert(ch->manifest_file_path == nullptr);
-
- for (size_t i = 0; i < len; i += 1) {
- Buf *buf = ptr[i];
- cache_file(ch, buf);
- }
- cache_str(ch, "");
-}
-
-void cache_list_of_str(CacheHash *ch, const char **ptr, size_t len) {
- assert(ch->manifest_file_path == nullptr);
-
- for (size_t i = 0; i < len; i += 1) {
- const char *s = ptr[i];
- cache_str(ch, s);
- }
- cache_str(ch, "");
-}
-
-void cache_file(CacheHash *ch, Buf *file_path) {
- assert(ch->manifest_file_path == nullptr);
- assert(file_path != nullptr);
- Buf *resolved_path = buf_alloc();
- *resolved_path = os_path_resolve(&file_path, 1);
- CacheHashFile *chf = ch->files.add_one();
- chf->path = resolved_path;
- cache_buf(ch, resolved_path);
-}
-
-void cache_file_opt(CacheHash *ch, Buf *file_path) {
- assert(ch->manifest_file_path == nullptr);
- if (file_path == nullptr) {
- cache_str(ch, "");
- cache_str(ch, "");
- } else {
- cache_file(ch, file_path);
- }
-}
-
-// Ported from std/base64.zig
-static uint8_t base64_fs_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
-static void base64_encode(Slice dest, Slice source) {
- size_t dest_len = ((source.len + 2) / 3) * 4;
- assert(dest.len == dest_len);
-
- size_t i = 0;
- size_t out_index = 0;
- for (; i + 2 < source.len; i += 3) {
- dest.ptr[out_index] = base64_fs_alphabet[(source.ptr[i] >> 2) & 0x3f];
- out_index += 1;
-
- dest.ptr[out_index] = base64_fs_alphabet[((source.ptr[i] & 0x3) << 4) | ((source.ptr[i + 1] & 0xf0) >> 4)];
- out_index += 1;
-
- dest.ptr[out_index] = base64_fs_alphabet[((source.ptr[i + 1] & 0xf) << 2) | ((source.ptr[i + 2] & 0xc0) >> 6)];
- out_index += 1;
-
- dest.ptr[out_index] = base64_fs_alphabet[source.ptr[i + 2] & 0x3f];
- out_index += 1;
- }
-
- // Assert that we never need pad characters.
- assert(i == source.len);
-}
-
-// Ported from std/base64.zig
-static Error base64_decode(Slice dest, Slice source) {
- if (source.len % 4 != 0)
- return ErrorInvalidFormat;
- if (dest.len != (source.len / 4) * 3)
- return ErrorInvalidFormat;
-
- // In Zig this is comptime computed. In C++ it's not worth it to do that.
- uint8_t char_to_index[256];
- bool char_in_alphabet[256] = {0};
- for (size_t i = 0; i < 64; i += 1) {
- uint8_t c = base64_fs_alphabet[i];
- assert(!char_in_alphabet[c]);
- char_in_alphabet[c] = true;
- char_to_index[c] = i;
- }
-
- size_t src_cursor = 0;
- size_t dest_cursor = 0;
-
- for (;src_cursor < source.len; src_cursor += 4) {
- if (!char_in_alphabet[source.ptr[src_cursor + 0]]) return ErrorInvalidFormat;
- if (!char_in_alphabet[source.ptr[src_cursor + 1]]) return ErrorInvalidFormat;
- if (!char_in_alphabet[source.ptr[src_cursor + 2]]) return ErrorInvalidFormat;
- if (!char_in_alphabet[source.ptr[src_cursor + 3]]) return ErrorInvalidFormat;
- dest.ptr[dest_cursor + 0] = (char_to_index[source.ptr[src_cursor + 0]] << 2) | (char_to_index[source.ptr[src_cursor + 1]] >> 4);
- dest.ptr[dest_cursor + 1] = (char_to_index[source.ptr[src_cursor + 1]] << 4) | (char_to_index[source.ptr[src_cursor + 2]] >> 2);
- dest.ptr[dest_cursor + 2] = (char_to_index[source.ptr[src_cursor + 2]] << 6) | (char_to_index[source.ptr[src_cursor + 3]]);
- dest_cursor += 3;
- }
-
- assert(src_cursor == source.len);
- assert(dest_cursor == dest.len);
- return ErrorNone;
-}
-
-static Error hash_file(uint8_t *digest, OsFile handle, Buf *contents) {
- Error err;
-
- if (contents) {
- buf_resize(contents, 0);
- }
-
- blake2b_state blake;
- int rc = blake2b_init(&blake, 48);
- assert(rc == 0);
-
- for (;;) {
- uint8_t buf[4096];
- size_t amt = 4096;
- if ((err = os_file_read(handle, buf, &amt)))
- return err;
- if (amt == 0) {
- rc = blake2b_final(&blake, digest, 48);
- assert(rc == 0);
- return ErrorNone;
- }
- blake2b_update(&blake, buf, amt);
- if (contents) {
- buf_append_mem(contents, (char*)buf, amt);
- }
- }
-}
-
-// If the wall clock time, rounded to the same precision as the
-// mtime, is equal to the mtime, then we cannot rely on this mtime
-// yet. We will instead save an mtime value that indicates the hash
-// must be unconditionally computed.
-static bool is_problematic_timestamp(const OsTimeStamp *fs_clock) {
- OsTimeStamp wall_clock = os_timestamp_calendar();
- // First make all the least significant zero bits in the fs_clock, also zero bits in the wall clock.
- if (fs_clock->nsec == 0) {
- wall_clock.nsec = 0;
- if (fs_clock->sec == 0) {
- wall_clock.sec = 0;
- } else {
- wall_clock.sec &= (-1ull) << ctzll(fs_clock->sec);
- }
- } else {
- wall_clock.nsec &= (-1ull) << ctzll(fs_clock->nsec);
- }
- return wall_clock.nsec == fs_clock->nsec && wall_clock.sec == fs_clock->sec;
-}
-
-static Error populate_file_hash(CacheHash *ch, CacheHashFile *chf, Buf *contents) {
- Error err;
-
- assert(chf->path != nullptr);
-
- OsFile this_file;
- if ((err = os_file_open_r(chf->path, &this_file, &chf->attr)))
- return err;
-
- if (is_problematic_timestamp(&chf->attr.mtime)) {
- chf->attr.mtime.sec = 0;
- chf->attr.mtime.nsec = 0;
- chf->attr.inode = 0;
- }
-
- if ((err = hash_file(chf->bin_digest, this_file, contents))) {
- os_file_close(&this_file);
- return err;
- }
- os_file_close(&this_file);
-
- blake2b_update(&ch->blake, chf->bin_digest, 48);
-
- return ErrorNone;
-}
-
-Error cache_hit(CacheHash *ch, Buf *out_digest) {
- Error err;
-
- uint8_t bin_digest[48];
- int rc = blake2b_final(&ch->blake, bin_digest, 48);
- assert(rc == 0);
-
- buf_resize(&ch->b64_digest, 64);
- base64_encode(buf_to_slice(&ch->b64_digest), {bin_digest, 48});
-
- if (ch->files.length == 0 && !ch->force_check_manifest) {
- buf_resize(out_digest, 64);
- base64_encode(buf_to_slice(out_digest), {bin_digest, 48});
- return ErrorNone;
- }
-
- rc = blake2b_init(&ch->blake, 48);
- assert(rc == 0);
- blake2b_update(&ch->blake, bin_digest, 48);
-
- ch->manifest_file_path = buf_alloc();
- os_path_join(ch->manifest_dir, &ch->b64_digest, ch->manifest_file_path);
-
- buf_append_str(ch->manifest_file_path, ".txt");
-
- if ((err = os_make_path(ch->manifest_dir)))
- return err;
-
- if ((err = os_file_open_lock_rw(ch->manifest_file_path, &ch->manifest_file)))
- return err;
-
- Buf line_buf = BUF_INIT;
- buf_resize(&line_buf, 512);
- if ((err = os_file_read_all(ch->manifest_file, &line_buf))) {
- os_file_close(&ch->manifest_file);
- return err;
- }
-
- size_t input_file_count = ch->files.length;
- bool any_file_changed = false;
- Error return_code = ErrorNone;
- size_t file_i = 0;
- SplitIterator line_it = memSplit(buf_to_slice(&line_buf), str("\n"));
- for (;; file_i += 1) {
- Optional> opt_line = SplitIterator_next(&line_it);
-
- CacheHashFile *chf;
- if (file_i < input_file_count) {
- chf = &ch->files.at(file_i);
- } else if (any_file_changed) {
- // cache miss.
- // keep the manifest file open with the rw lock
- // reset the hash
- rc = blake2b_init(&ch->blake, 48);
- assert(rc == 0);
- blake2b_update(&ch->blake, bin_digest, 48);
- ch->files.resize(input_file_count);
- // bring the hash up to the input file hashes
- for (file_i = 0; file_i < input_file_count; file_i += 1) {
- blake2b_update(&ch->blake, ch->files.at(file_i).bin_digest, 48);
- }
- // caller can notice that out_digest is unmodified.
- return return_code;
- } else if (!opt_line.is_some) {
- break;
- } else {
- chf = ch->files.add_one();
- chf->path = nullptr;
- }
-
- if (!opt_line.is_some)
- break;
-
- SplitIterator it = memSplit(opt_line.value, str(" "));
-
- Optional> opt_inode = SplitIterator_next(&it);
- if (!opt_inode.is_some) {
- return_code = ErrorInvalidFormat;
- break;
- }
- chf->attr.inode = strtoull((const char *)opt_inode.value.ptr, nullptr, 10);
-
- Optional> opt_mtime_sec = SplitIterator_next(&it);
- if (!opt_mtime_sec.is_some) {
- return_code = ErrorInvalidFormat;
- break;
- }
- chf->attr.mtime.sec = strtoull((const char *)opt_mtime_sec.value.ptr, nullptr, 10);
-
- Optional> opt_mtime_nsec = SplitIterator_next(&it);
- if (!opt_mtime_nsec.is_some) {
- return_code = ErrorInvalidFormat;
- break;
- }
- chf->attr.mtime.nsec = strtoull((const char *)opt_mtime_nsec.value.ptr, nullptr, 10);
-
- Optional> opt_digest = SplitIterator_next(&it);
- if (!opt_digest.is_some) {
- return_code = ErrorInvalidFormat;
- break;
- }
- if ((err = base64_decode({chf->bin_digest, 48}, opt_digest.value))) {
- return_code = ErrorInvalidFormat;
- break;
- }
-
- Slice file_path = SplitIterator_rest(&it);
- if (file_path.len == 0) {
- return_code = ErrorInvalidFormat;
- break;
- }
- Buf *this_path = buf_create_from_slice(file_path);
- if (chf->path != nullptr && !buf_eql_buf(this_path, chf->path)) {
- return_code = ErrorInvalidFormat;
- break;
- }
- chf->path = this_path;
-
- // if the mtime matches we can trust the digest
- OsFile this_file;
- OsFileAttr actual_attr;
- if ((err = os_file_open_r(chf->path, &this_file, &actual_attr))) {
- fprintf(stderr, "Unable to open %s\n: %s", buf_ptr(chf->path), err_str(err));
- os_file_close(&ch->manifest_file);
- return ErrorCacheUnavailable;
- }
- if (chf->attr.mtime.sec == actual_attr.mtime.sec &&
- chf->attr.mtime.nsec == actual_attr.mtime.nsec &&
- chf->attr.inode == actual_attr.inode)
- {
- os_file_close(&this_file);
- } else {
- // we have to recompute the digest.
- // later we'll rewrite the manifest with the new mtime/digest values
- ch->manifest_dirty = true;
- chf->attr = actual_attr;
-
- if (is_problematic_timestamp(&actual_attr.mtime)) {
- chf->attr.mtime.sec = 0;
- chf->attr.mtime.nsec = 0;
- chf->attr.inode = 0;
- }
-
- uint8_t actual_digest[48];
- if ((err = hash_file(actual_digest, this_file, nullptr))) {
- os_file_close(&this_file);
- os_file_close(&ch->manifest_file);
- return err;
- }
- os_file_close(&this_file);
- if (memcmp(chf->bin_digest, actual_digest, 48) != 0) {
- memcpy(chf->bin_digest, actual_digest, 48);
- // keep going until we have the input file digests
- any_file_changed = true;
- }
- }
- if (!any_file_changed) {
- blake2b_update(&ch->blake, chf->bin_digest, 48);
- }
- }
- if (file_i < input_file_count || file_i == 0 || return_code != ErrorNone) {
- // manifest file is empty or missing entries, so this is a cache miss
- ch->manifest_dirty = true;
- for (; file_i < input_file_count; file_i += 1) {
- CacheHashFile *chf = &ch->files.at(file_i);
- if ((err = populate_file_hash(ch, chf, nullptr))) {
- fprintf(stderr, "Unable to hash %s: %s\n", buf_ptr(chf->path), err_str(err));
- os_file_close(&ch->manifest_file);
- return ErrorCacheUnavailable;
- }
- }
- if (return_code != ErrorNone && return_code != ErrorInvalidFormat) {
- os_file_close(&ch->manifest_file);
- }
- return return_code;
- }
- // Cache Hit
- return cache_final(ch, out_digest);
-}
-
-Error cache_add_file_fetch(CacheHash *ch, Buf *resolved_path, Buf *contents) {
- Error err;
-
- assert(ch->manifest_file_path != nullptr);
- CacheHashFile *chf = ch->files.add_one();
- chf->path = resolved_path;
- if ((err = populate_file_hash(ch, chf, contents))) {
- os_file_close(&ch->manifest_file);
- return err;
- }
-
- return ErrorNone;
-}
-
-Error cache_add_file(CacheHash *ch, Buf *path) {
- Buf *resolved_path = buf_alloc();
- *resolved_path = os_path_resolve(&path, 1);
- return cache_add_file_fetch(ch, resolved_path, nullptr);
-}
-
-Error cache_add_dep_file(CacheHash *ch, Buf *dep_file_path, bool verbose) {
- Error err;
- Buf *contents = buf_alloc();
- if ((err = os_fetch_file_path(dep_file_path, contents))) {
- if (err == ErrorFileNotFound)
- return err;
- if (verbose) {
- fprintf(stderr, "%s: unable to read .d file: %s\n", err_str(err), buf_ptr(dep_file_path));
- }
- return ErrorReadingDepFile;
- }
- auto it = stage2_DepTokenizer_init(buf_ptr(contents), buf_len(contents));
- // skip first token: target
- {
- auto result = stage2_DepTokenizer_next(&it);
- switch (result.type_id) {
- case stage2_DepNextResult::error:
- if (verbose) {
- fprintf(stderr, "%s: failed processing .d file: %s\n", result.textz, buf_ptr(dep_file_path));
- }
- err = ErrorInvalidDepFile;
- goto finish;
- case stage2_DepNextResult::null:
- err = ErrorNone;
- goto finish;
- case stage2_DepNextResult::target:
- case stage2_DepNextResult::prereq:
- err = ErrorNone;
- break;
- }
- }
- // Process 0+ preqreqs.
- // clang is invoked in single-source mode so we never get more targets.
- for (;;) {
- auto result = stage2_DepTokenizer_next(&it);
- switch (result.type_id) {
- case stage2_DepNextResult::error:
- if (verbose) {
- fprintf(stderr, "%s: failed processing .d file: %s\n", result.textz, buf_ptr(dep_file_path));
- }
- err = ErrorInvalidDepFile;
- goto finish;
- case stage2_DepNextResult::null:
- case stage2_DepNextResult::target:
- err = ErrorNone;
- goto finish;
- case stage2_DepNextResult::prereq:
- break;
- }
- auto textbuf = buf_alloc();
- buf_init_from_str(textbuf, result.textz);
- if ((err = cache_add_file(ch, textbuf))) {
- if (verbose) {
- fprintf(stderr, "unable to add %s to cache: %s\n", result.textz, err_str(err));
- fprintf(stderr, "when processing .d file: %s\n", buf_ptr(dep_file_path));
- }
- goto finish;
- }
- }
-
- finish:
- stage2_DepTokenizer_deinit(&it);
- return err;
-}
-
-static Error write_manifest_file(CacheHash *ch) {
- Error err;
- Buf contents = BUF_INIT;
- buf_resize(&contents, 0);
- uint8_t encoded_digest[65];
- encoded_digest[64] = 0;
- for (size_t i = 0; i < ch->files.length; i += 1) {
- CacheHashFile *chf = &ch->files.at(i);
- base64_encode({encoded_digest, 64}, {chf->bin_digest, 48});
- buf_appendf(&contents, "%" ZIG_PRI_u64 " %" ZIG_PRI_u64 " %" ZIG_PRI_u64 " %s %s\n",
- chf->attr.inode, chf->attr.mtime.sec, chf->attr.mtime.nsec, encoded_digest, buf_ptr(chf->path));
- }
- if ((err = os_file_overwrite(ch->manifest_file, &contents)))
- return err;
-
- return ErrorNone;
-}
-
-Error cache_final(CacheHash *ch, Buf *out_digest) {
- assert(ch->manifest_file_path != nullptr);
-
- // We don't close the manifest file yet, because we want to
- // keep it locked until the API user is done using it.
- // We also don't write out the manifest yet, because until
- // cache_release is called we still might be working on creating
- // the artifacts to cache.
-
- uint8_t bin_digest[48];
- int rc = blake2b_final(&ch->blake, bin_digest, 48);
- assert(rc == 0);
- buf_resize(out_digest, 64);
- base64_encode(buf_to_slice(out_digest), {bin_digest, 48});
-
- return ErrorNone;
-}
-
-void cache_release(CacheHash *ch) {
- assert(ch->manifest_file_path != nullptr);
-
- Error err;
-
- if (ch->manifest_dirty) {
- if ((err = write_manifest_file(ch))) {
- fprintf(stderr, "Warning: Unable to write cache file '%s': %s\n",
- buf_ptr(ch->manifest_file_path), err_str(err));
- }
- }
-
- os_file_close(&ch->manifest_file);
-}
-
diff --git a/src/cache_hash.hpp b/src/cache_hash.hpp
deleted file mode 100644
index ba2434076a..0000000000
--- a/src/cache_hash.hpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (c) 2018 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#ifndef ZIG_CACHE_HASH_HPP
-#define ZIG_CACHE_HASH_HPP
-
-#include "blake2.h"
-#include "os.hpp"
-
-struct LinkLib;
-
-struct CacheHashFile {
- Buf *path;
- OsFileAttr attr;
- uint8_t bin_digest[48];
- Buf *contents;
-};
-
-struct CacheHash {
- blake2b_state blake;
- ZigList files;
- Buf *manifest_dir;
- Buf *manifest_file_path;
- Buf b64_digest;
- OsFile manifest_file;
- bool manifest_dirty;
- bool force_check_manifest;
-};
-
-// Always call this first to set up.
-void cache_init(CacheHash *ch, Buf *manifest_dir);
-
-// Next, use the hash population functions to add the initial parameters.
-void cache_mem(CacheHash *ch, const char *ptr, size_t len);
-void cache_slice(CacheHash *ch, Slice slice);
-void cache_str(CacheHash *ch, const char *ptr);
-void cache_int(CacheHash *ch, int x);
-void cache_bool(CacheHash *ch, bool x);
-void cache_usize(CacheHash *ch, size_t x);
-void cache_buf(CacheHash *ch, Buf *buf);
-void cache_buf_opt(CacheHash *ch, Buf *buf);
-void cache_list_of_link_lib(CacheHash *ch, LinkLib **ptr, size_t len);
-void cache_list_of_buf(CacheHash *ch, Buf **ptr, size_t len);
-void cache_list_of_file(CacheHash *ch, Buf **ptr, size_t len);
-void cache_list_of_str(CacheHash *ch, const char **ptr, size_t len);
-void cache_file(CacheHash *ch, Buf *path);
-void cache_file_opt(CacheHash *ch, Buf *path);
-
-// Then call cache_hit when you're ready to see if you can skip the next step.
-// out_b64_digest will be left unchanged if it was a cache miss.
-// If you got a cache hit, the next step is cache_release.
-// From this point on, there is a lock on the input params. Release
-// the lock with cache_release.
-// Set force_check_manifest if you plan to add files later, but have not
-// added any files before calling cache_hit. CacheHash::b64_digest becomes
-// available for use after this call, even in the case of a miss, and it
-// is a hash of the input parameters only.
-// If this function returns ErrorInvalidFormat, that error may be treated
-// as a cache miss.
-Error ATTRIBUTE_MUST_USE cache_hit(CacheHash *ch, Buf *out_b64_digest);
-
-// If you did not get a cache hit, call this function for every file
-// that is depended on, and then finish with cache_final.
-Error ATTRIBUTE_MUST_USE cache_add_file(CacheHash *ch, Buf *path);
-// This opens a file created by -MD -MF args to Clang
-Error ATTRIBUTE_MUST_USE cache_add_dep_file(CacheHash *ch, Buf *path, bool verbose);
-
-// This variant of cache_add_file returns the file contents.
-// Also the file path argument must be already resolved.
-Error ATTRIBUTE_MUST_USE cache_add_file_fetch(CacheHash *ch, Buf *resolved_path, Buf *contents);
-
-// out_b64_digest will be the same thing that cache_hit returns if you got a cache hit
-Error ATTRIBUTE_MUST_USE cache_final(CacheHash *ch, Buf *out_b64_digest);
-
-// Until this function is called, no one will be able to get a lock on your input params.
-void cache_release(CacheHash *ch);
-
-
-#endif
diff --git a/src-self-hosted/clang.zig b/src/clang.zig
similarity index 100%
rename from src-self-hosted/clang.zig
rename to src/clang.zig
diff --git a/src-self-hosted/clang_options.zig b/src/clang_options.zig
similarity index 93%
rename from src-self-hosted/clang_options.zig
rename to src/clang_options.zig
index 1b70c71dac..42bfecb746 100644
--- a/src-self-hosted/clang_options.zig
+++ b/src/clang_options.zig
@@ -7,9 +7,7 @@ pub const CliArg = struct {
name: []const u8,
syntax: Syntax,
- /// TODO we're going to want to change this when we start shipping self-hosted because this causes
- /// all the functions in stage2.zig to get exported.
- zig_equivalent: @import("stage2.zig").ClangArgIterator.ZigEquivalent,
+ zig_equivalent: @import("main.zig").ClangArgIterator.ZigEquivalent,
/// Prefixed by "-"
pd1: bool = false,
diff --git a/src-self-hosted/clang_options_data.zig b/src/clang_options_data.zig
similarity index 99%
rename from src-self-hosted/clang_options_data.zig
rename to src/clang_options_data.zig
index 53fd8eb028..5a3e3a0e87 100644
--- a/src-self-hosted/clang_options_data.zig
+++ b/src/clang_options_data.zig
@@ -7,7 +7,7 @@ flagpd1("CC"),
.{
.name = "E",
.syntax = .flag,
- .zig_equivalent = .pp_or_asm,
+ .zig_equivalent = .preprocess_only,
.pd1 = true,
.pd2 = false,
.psl = false,
@@ -96,7 +96,7 @@ flagpd1("Qy"),
.{
.name = "S",
.syntax = .flag,
- .zig_equivalent = .pp_or_asm,
+ .zig_equivalent = .asm_only,
.pd1 = true,
.pd2 = false,
.psl = false,
@@ -199,7 +199,7 @@ sepd1("Zlinker-input"),
.{
.name = "E",
.syntax = .flag,
- .zig_equivalent = .pp_or_asm,
+ .zig_equivalent = .preprocess_only,
.pd1 = true,
.pd2 = false,
.psl = true,
@@ -1512,7 +1512,7 @@ flagpsl("MT"),
.{
.name = "assemble",
.syntax = .flag,
- .zig_equivalent = .pp_or_asm,
+ .zig_equivalent = .asm_only,
.pd1 = false,
.pd2 = true,
.psl = false,
@@ -1840,7 +1840,7 @@ flagpsl("MT"),
.{
.name = "preprocess",
.syntax = .flag,
- .zig_equivalent = .pp_or_asm,
+ .zig_equivalent = .preprocess_only,
.pd1 = false,
.pd2 = true,
.psl = false,
diff --git a/src/codegen.hpp b/src/codegen.hpp
deleted file mode 100644
index 3139071d52..0000000000
--- a/src/codegen.hpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (c) 2015 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#ifndef ZIG_CODEGEN_HPP
-#define ZIG_CODEGEN_HPP
-
-#include "parser.hpp"
-#include "errmsg.hpp"
-#include "target.hpp"
-#include "stage2.h"
-
-#include
-
-CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target,
- OutType out_type, BuildMode build_mode, Buf *zig_lib_dir,
- Stage2LibCInstallation *libc, Buf *cache_dir, bool is_test_build, Stage2ProgressNode *progress_node);
-
-CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType out_type,
- Stage2LibCInstallation *libc, const char *name, Stage2ProgressNode *progress_node);
-
-void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len);
-void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len);
-void codegen_set_each_lib_rpath(CodeGen *codegen, bool each_lib_rpath);
-
-void codegen_set_strip(CodeGen *codegen, bool strip);
-void codegen_set_errmsg_color(CodeGen *codegen, ErrColor err_color);
-void codegen_set_out_name(CodeGen *codegen, Buf *out_name);
-void codegen_add_lib_dir(CodeGen *codegen, const char *dir);
-void codegen_add_forbidden_lib(CodeGen *codegen, Buf *lib);
-LinkLib *codegen_add_link_lib(CodeGen *codegen, Buf *lib);
-void codegen_add_framework(CodeGen *codegen, const char *name);
-void codegen_add_rpath(CodeGen *codegen, const char *name);
-void codegen_set_rdynamic(CodeGen *g, bool rdynamic);
-void codegen_set_linker_script(CodeGen *g, const char *linker_script);
-void codegen_set_test_filter(CodeGen *g, Buf *filter);
-void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix);
-void codegen_set_lib_version(CodeGen *g, bool is_versioned, size_t major, size_t minor, size_t patch);
-void codegen_add_time_event(CodeGen *g, const char *name);
-void codegen_print_timing_report(CodeGen *g, FILE *f);
-void codegen_link(CodeGen *g);
-void zig_link_add_compiler_rt(CodeGen *g, Stage2ProgressNode *progress_node);
-void codegen_build_and_link(CodeGen *g);
-
-ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path,
- const char *pkg_path);
-void codegen_add_assembly(CodeGen *g, Buf *path);
-void codegen_add_object(CodeGen *g, Buf *object_path);
-
-void codegen_translate_c(CodeGen *g, Buf *full_path);
-
-Buf *codegen_generate_builtin_source(CodeGen *g);
-
-TargetSubsystem detect_subsystem(CodeGen *g);
-
-void codegen_release_caches(CodeGen *codegen);
-bool codegen_fn_has_err_ret_tracing_arg(CodeGen *g, ZigType *return_type);
-bool codegen_fn_has_err_ret_tracing_stack(CodeGen *g, ZigFn *fn, bool is_async);
-
-ATTRIBUTE_NORETURN
-void codegen_report_errors_and_exit(CodeGen *g);
-
-void codegen_switch_sub_prog_node(CodeGen *g, Stage2ProgressNode *node);
-
-#endif
diff --git a/src-self-hosted/codegen.zig b/src/codegen.zig
similarity index 99%
rename from src-self-hosted/codegen.zig
rename to src/codegen.zig
index 9405a5f72c..a1d3cc2fc4 100644
--- a/src-self-hosted/codegen.zig
+++ b/src/codegen.zig
@@ -8,7 +8,8 @@ const Value = @import("value.zig").Value;
const TypedValue = @import("TypedValue.zig");
const link = @import("link.zig");
const Module = @import("Module.zig");
-const ErrorMsg = Module.ErrorMsg;
+const Compilation = @import("Compilation.zig");
+const ErrorMsg = Compilation.ErrorMsg;
const Target = std.Target;
const Allocator = mem.Allocator;
const trace = @import("tracy.zig").trace;
@@ -50,7 +51,7 @@ pub const Result = union(enum) {
appended: void,
/// The value is available externally, `code` is unused.
externally_managed: []const u8,
- fail: *Module.ErrorMsg,
+ fail: *ErrorMsg,
};
pub const GenerateSymbolError = error{
diff --git a/src-self-hosted/codegen/arm.zig b/src/codegen/arm.zig
similarity index 100%
rename from src-self-hosted/codegen/arm.zig
rename to src/codegen/arm.zig
diff --git a/src-self-hosted/codegen/c.zig b/src/codegen/c.zig
similarity index 100%
rename from src-self-hosted/codegen/c.zig
rename to src/codegen/c.zig
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
new file mode 100644
index 0000000000..01fa0baf02
--- /dev/null
+++ b/src/codegen/llvm.zig
@@ -0,0 +1,125 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+
+pub fn targetTriple(allocator: *Allocator, target: std.Target) ![]u8 {
+ const llvm_arch = switch (target.cpu.arch) {
+ .arm => "arm",
+ .armeb => "armeb",
+ .aarch64 => "aarch64",
+ .aarch64_be => "aarch64_be",
+ .aarch64_32 => "aarch64_32",
+ .arc => "arc",
+ .avr => "avr",
+ .bpfel => "bpfel",
+ .bpfeb => "bpfeb",
+ .hexagon => "hexagon",
+ .mips => "mips",
+ .mipsel => "mipsel",
+ .mips64 => "mips64",
+ .mips64el => "mips64el",
+ .msp430 => "msp430",
+ .powerpc => "powerpc",
+ .powerpc64 => "powerpc64",
+ .powerpc64le => "powerpc64le",
+ .r600 => "r600",
+ .amdgcn => "amdgcn",
+ .riscv32 => "riscv32",
+ .riscv64 => "riscv64",
+ .sparc => "sparc",
+ .sparcv9 => "sparcv9",
+ .sparcel => "sparcel",
+ .s390x => "s390x",
+ .tce => "tce",
+ .tcele => "tcele",
+ .thumb => "thumb",
+ .thumbeb => "thumbeb",
+ .i386 => "i386",
+ .x86_64 => "x86_64",
+ .xcore => "xcore",
+ .nvptx => "nvptx",
+ .nvptx64 => "nvptx64",
+ .le32 => "le32",
+ .le64 => "le64",
+ .amdil => "amdil",
+ .amdil64 => "amdil64",
+ .hsail => "hsail",
+ .hsail64 => "hsail64",
+ .spir => "spir",
+ .spir64 => "spir64",
+ .kalimba => "kalimba",
+ .shave => "shave",
+ .lanai => "lanai",
+ .wasm32 => "wasm32",
+ .wasm64 => "wasm64",
+ .renderscript32 => "renderscript32",
+ .renderscript64 => "renderscript64",
+ .ve => "ve",
+ .spu_2 => return error.LLVMBackendDoesNotSupportSPUMarkII,
+ };
+ // TODO Add a sub-arch for some architectures depending on CPU features.
+
+ const llvm_os = switch (target.os.tag) {
+ .freestanding => "unknown",
+ .ananas => "ananas",
+ .cloudabi => "cloudabi",
+ .dragonfly => "dragonfly",
+ .freebsd => "freebsd",
+ .fuchsia => "fuchsia",
+ .ios => "ios",
+ .kfreebsd => "kfreebsd",
+ .linux => "linux",
+ .lv2 => "lv2",
+ .macosx => "macosx",
+ .netbsd => "netbsd",
+ .openbsd => "openbsd",
+ .solaris => "solaris",
+ .windows => "windows",
+ .haiku => "haiku",
+ .minix => "minix",
+ .rtems => "rtems",
+ .nacl => "nacl",
+ .cnk => "cnk",
+ .aix => "aix",
+ .cuda => "cuda",
+ .nvcl => "nvcl",
+ .amdhsa => "amdhsa",
+ .ps4 => "ps4",
+ .elfiamcu => "elfiamcu",
+ .tvos => "tvos",
+ .watchos => "watchos",
+ .mesa3d => "mesa3d",
+ .contiki => "contiki",
+ .amdpal => "amdpal",
+ .hermit => "hermit",
+ .hurd => "hurd",
+ .wasi => "wasi",
+ .emscripten => "emscripten",
+ .uefi => "windows",
+ .other => "unknown",
+ };
+
+ const llvm_abi = switch (target.abi) {
+ .none => "unknown",
+ .gnu => "gnu",
+ .gnuabin32 => "gnuabin32",
+ .gnuabi64 => "gnuabi64",
+ .gnueabi => "gnueabi",
+ .gnueabihf => "gnueabihf",
+ .gnux32 => "gnux32",
+ .code16 => "code16",
+ .eabi => "eabi",
+ .eabihf => "eabihf",
+ .android => "android",
+ .musl => "musl",
+ .musleabi => "musleabi",
+ .musleabihf => "musleabihf",
+ .msvc => "msvc",
+ .itanium => "itanium",
+ .cygnus => "cygnus",
+ .coreclr => "coreclr",
+ .simulator => "simulator",
+ .macabi => "macabi",
+ };
+
+ return std.fmt.allocPrint(allocator, "{}-unknown-{}-{}", .{ llvm_arch, llvm_os, llvm_abi });
+}
diff --git a/src-self-hosted/codegen/riscv64.zig b/src/codegen/riscv64.zig
similarity index 100%
rename from src-self-hosted/codegen/riscv64.zig
rename to src/codegen/riscv64.zig
diff --git a/src-self-hosted/codegen/spu-mk2.zig b/src/codegen/spu-mk2.zig
similarity index 100%
rename from src-self-hosted/codegen/spu-mk2.zig
rename to src/codegen/spu-mk2.zig
diff --git a/src-self-hosted/codegen/spu-mk2/interpreter.zig b/src/codegen/spu-mk2/interpreter.zig
similarity index 100%
rename from src-self-hosted/codegen/spu-mk2/interpreter.zig
rename to src/codegen/spu-mk2/interpreter.zig
diff --git a/src-self-hosted/codegen/wasm.zig b/src/codegen/wasm.zig
similarity index 98%
rename from src-self-hosted/codegen/wasm.zig
rename to src/codegen/wasm.zig
index e55e904934..4ea8838409 100644
--- a/src-self-hosted/codegen/wasm.zig
+++ b/src/codegen/wasm.zig
@@ -5,7 +5,8 @@ const assert = std.debug.assert;
const leb = std.debug.leb;
const mem = std.mem;
-const Decl = @import("../Module.zig").Decl;
+const Module = @import("../Module.zig");
+const Decl = Module.Decl;
const Inst = @import("../ir.zig").Inst;
const Type = @import("../type.zig").Type;
const Value = @import("../value.zig").Value;
diff --git a/src-self-hosted/codegen/x86.zig b/src/codegen/x86.zig
similarity index 100%
rename from src-self-hosted/codegen/x86.zig
rename to src/codegen/x86.zig
diff --git a/src-self-hosted/codegen/x86_64.zig b/src/codegen/x86_64.zig
similarity index 100%
rename from src-self-hosted/codegen/x86_64.zig
rename to src/codegen/x86_64.zig
diff --git a/src/compiler.cpp b/src/compiler.cpp
deleted file mode 100644
index 6c477a1506..0000000000
--- a/src/compiler.cpp
+++ /dev/null
@@ -1,196 +0,0 @@
-#include "cache_hash.hpp"
-#include "os.hpp"
-#include "compiler.hpp"
-
-#include
-
-Error get_compiler_id(Buf **result) {
- static Buf saved_compiler_id = BUF_INIT;
-
- if (saved_compiler_id.list.length != 0) {
- *result = &saved_compiler_id;
- return ErrorNone;
- }
-
- Error err;
- Buf *manifest_dir = buf_alloc();
- os_path_join(get_global_cache_dir(), buf_create_from_str("exe"), manifest_dir);
-
- CacheHash cache_hash;
- CacheHash *ch = &cache_hash;
- cache_init(ch, manifest_dir);
- Buf self_exe_path = BUF_INIT;
- if ((err = os_self_exe_path(&self_exe_path)))
- return err;
-
- cache_file(ch, &self_exe_path);
-
- buf_resize(&saved_compiler_id, 0);
- if ((err = cache_hit(ch, &saved_compiler_id))) {
- if (err != ErrorInvalidFormat)
- return err;
- }
- if (buf_len(&saved_compiler_id) != 0) {
- cache_release(ch);
- *result = &saved_compiler_id;
- return ErrorNone;
- }
- ZigList lib_paths = {};
- if ((err = os_self_exe_shared_libs(lib_paths)))
- return err;
- #if defined(ZIG_OS_DARWIN)
- // only add the self exe path on mac os
- Buf *lib_path = lib_paths.at(0);
- if ((err = cache_add_file(ch, lib_path)))
- return err;
- #else
- for (size_t i = 0; i < lib_paths.length; i += 1) {
- Buf *lib_path = lib_paths.at(i);
- if ((err = cache_add_file(ch, lib_path)))
- return err;
- }
- #endif
-
- if ((err = cache_final(ch, &saved_compiler_id)))
- return err;
-
- cache_release(ch);
-
- *result = &saved_compiler_id;
- return ErrorNone;
-}
-
-static bool test_zig_install_prefix(Buf *test_path, Buf *out_zig_lib_dir) {
- {
- Buf *test_zig_dir = buf_sprintf("%s" OS_SEP "lib" OS_SEP "zig", buf_ptr(test_path));
- Buf *test_index_file = buf_sprintf("%s" OS_SEP "std" OS_SEP "std.zig", buf_ptr(test_zig_dir));
- int err;
- bool exists;
- if ((err = os_file_exists(test_index_file, &exists))) {
- exists = false;
- }
- if (exists) {
- buf_init_from_buf(out_zig_lib_dir, test_zig_dir);
- return true;
- }
- }
-
- // Also try without "zig"
- {
- Buf *test_zig_dir = buf_sprintf("%s" OS_SEP "lib", buf_ptr(test_path));
- Buf *test_index_file = buf_sprintf("%s" OS_SEP "std" OS_SEP "std.zig", buf_ptr(test_zig_dir));
- int err;
- bool exists;
- if ((err = os_file_exists(test_index_file, &exists))) {
- exists = false;
- }
- if (exists) {
- buf_init_from_buf(out_zig_lib_dir, test_zig_dir);
- return true;
- }
- }
-
- return false;
-}
-
-static int find_zig_lib_dir(Buf *out_path) {
- int err;
-
- Buf self_exe_path = BUF_INIT;
- buf_resize(&self_exe_path, 0);
- if (!(err = os_self_exe_path(&self_exe_path))) {
- Buf *cur_path = &self_exe_path;
-
- for (;;) {
- Buf *test_dir = buf_alloc();
- os_path_dirname(cur_path, test_dir);
-
- if (buf_eql_buf(test_dir, cur_path)) {
- break;
- }
-
- if (test_zig_install_prefix(test_dir, out_path)) {
- return 0;
- }
-
- cur_path = test_dir;
- }
- }
-
- return ErrorFileNotFound;
-}
-
-Buf *get_zig_lib_dir(void) {
- static Buf saved_lib_dir = BUF_INIT;
- if (saved_lib_dir.list.length != 0)
- return &saved_lib_dir;
- buf_resize(&saved_lib_dir, 0);
-
- int err;
- if ((err = find_zig_lib_dir(&saved_lib_dir))) {
- fprintf(stderr, "Unable to find zig lib directory\n");
- exit(EXIT_FAILURE);
- }
- return &saved_lib_dir;
-}
-
-Buf *get_zig_std_dir(Buf *zig_lib_dir) {
- static Buf saved_std_dir = BUF_INIT;
- if (saved_std_dir.list.length != 0)
- return &saved_std_dir;
- buf_resize(&saved_std_dir, 0);
-
- os_path_join(zig_lib_dir, buf_create_from_str("std"), &saved_std_dir);
-
- return &saved_std_dir;
-}
-
-Buf *get_zig_special_dir(Buf *zig_lib_dir) {
- static Buf saved_special_dir = BUF_INIT;
- if (saved_special_dir.list.length != 0)
- return &saved_special_dir;
- buf_resize(&saved_special_dir, 0);
-
- os_path_join(get_zig_std_dir(zig_lib_dir), buf_sprintf("special"), &saved_special_dir);
-
- return &saved_special_dir;
-}
-
-Buf *get_global_cache_dir(void) {
- static Buf saved_global_cache_dir = BUF_INIT;
- if (saved_global_cache_dir.list.length != 0)
- return &saved_global_cache_dir;
- buf_resize(&saved_global_cache_dir, 0);
-
- Buf app_data_dir = BUF_INIT;
- Error err;
- if ((err = os_get_app_data_dir(&app_data_dir, "zig"))) {
- fprintf(stderr, "Unable to get application data dir: %s\n", err_str(err));
- exit(1);
- }
- os_path_join(&app_data_dir, buf_create_from_str("stage1"), &saved_global_cache_dir);
- buf_deinit(&app_data_dir);
- return &saved_global_cache_dir;
-}
-
-FileExt classify_file_ext(const char *filename_ptr, size_t filename_len) {
- if (mem_ends_with_str(filename_ptr, filename_len, ".c")) {
- return FileExtC;
- } else if (mem_ends_with_str(filename_ptr, filename_len, ".C") ||
- mem_ends_with_str(filename_ptr, filename_len, ".cc") ||
- mem_ends_with_str(filename_ptr, filename_len, ".cpp") ||
- mem_ends_with_str(filename_ptr, filename_len, ".cxx"))
- {
- return FileExtCpp;
- } else if (mem_ends_with_str(filename_ptr, filename_len, ".ll")) {
- return FileExtLLVMIr;
- } else if (mem_ends_with_str(filename_ptr, filename_len, ".bc")) {
- return FileExtLLVMBitCode;
- } else if (mem_ends_with_str(filename_ptr, filename_len, ".s") ||
- mem_ends_with_str(filename_ptr, filename_len, ".S"))
- {
- return FileExtAsm;
- }
- // TODO look for .so, .so.X, .so.X.Y, .so.X.Y.Z
- return FileExtUnknown;
-}
diff --git a/src/compiler.hpp b/src/compiler.hpp
deleted file mode 100644
index ae2e6e9c5e..0000000000
--- a/src/compiler.hpp
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (c) 2018 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#ifndef ZIG_COMPILER_HPP
-#define ZIG_COMPILER_HPP
-
-#include "all_types.hpp"
-
-Error get_compiler_id(Buf **result);
-
-Buf *get_zig_lib_dir(void);
-Buf *get_zig_special_dir(Buf *zig_lib_dir);
-Buf *get_zig_std_dir(Buf *zig_lib_dir);
-
-Buf *get_global_cache_dir(void);
-
-
-FileExt classify_file_ext(const char *filename_ptr, size_t filename_len);
-
-#endif
diff --git a/src/config.zig.in b/src/config.zig.in
index ccb618df2d..9e574bc1e8 100644
--- a/src/config.zig.in
+++ b/src/config.zig.in
@@ -1,3 +1,6 @@
+pub const have_llvm = true;
pub const version: []const u8 = "@ZIG_VERSION@";
pub const log_scopes: []const []const u8 = &[_][]const u8{};
+pub const zir_dumps: []const []const u8 = &[_][]const u8{};
pub const enable_tracy = false;
+pub const is_stage1 = true;
diff --git a/src/glibc.cpp b/src/glibc.cpp
deleted file mode 100644
index 62f5604ba7..0000000000
--- a/src/glibc.cpp
+++ /dev/null
@@ -1,392 +0,0 @@
-/*
- * Copyright (c) 2019 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#include "glibc.hpp"
-#include "compiler.hpp"
-#include "cache_hash.hpp"
-#include "codegen.hpp"
-
-static const ZigGLibCLib glibc_libs[] = {
- {"c", 6},
- {"m", 6},
- {"pthread", 0},
- {"dl", 2},
- {"rt", 1},
- {"ld", 2},
- {"util", 1},
-};
-
-Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbose) {
- Error err;
-
- ZigGLibCAbi *glibc_abi = heap::c_allocator.create();
- glibc_abi->vers_txt_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "glibc" OS_SEP "vers.txt", buf_ptr(zig_lib_dir));
- glibc_abi->fns_txt_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "glibc" OS_SEP "fns.txt", buf_ptr(zig_lib_dir));
- glibc_abi->abi_txt_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "glibc" OS_SEP "abi.txt", buf_ptr(zig_lib_dir));
- glibc_abi->version_table.init(16);
-
- Buf *vers_txt_contents = buf_alloc();
- if ((err = os_fetch_file_path(glibc_abi->vers_txt_path, vers_txt_contents))) {
- if (verbose) {
- fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(glibc_abi->vers_txt_path), err_str(err));
- }
- return err;
- }
- Buf *fns_txt_contents = buf_alloc();
- if ((err = os_fetch_file_path(glibc_abi->fns_txt_path, fns_txt_contents))) {
- if (verbose) {
- fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(glibc_abi->fns_txt_path), err_str(err));
- }
- return err;
- }
- Buf *abi_txt_contents = buf_alloc();
- if ((err = os_fetch_file_path(glibc_abi->abi_txt_path, abi_txt_contents))) {
- if (verbose) {
- fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(glibc_abi->abi_txt_path), err_str(err));
- }
- return err;
- }
-
- {
- SplitIterator it = memSplit(buf_to_slice(vers_txt_contents), str("\r\n"));
- for (;;) {
- Optional> opt_component = SplitIterator_next(&it);
- if (!opt_component.is_some) break;
- Buf *ver_buf = buf_create_from_slice(opt_component.value);
- Stage2SemVer *this_ver = glibc_abi->all_versions.add_one();
- if ((err = target_parse_glibc_version(this_ver, buf_ptr(ver_buf)))) {
- if (verbose) {
- fprintf(stderr, "Unable to parse glibc version '%s': %s\n", buf_ptr(ver_buf), err_str(err));
- }
- return err;
- }
- }
- }
- {
- SplitIterator it = memSplit(buf_to_slice(fns_txt_contents), str("\r\n"));
- for (;;) {
- Optional> opt_component = SplitIterator_next(&it);
- if (!opt_component.is_some) break;
- SplitIterator line_it = memSplit(opt_component.value, str(" "));
- Optional> opt_fn_name = SplitIterator_next(&line_it);
- if (!opt_fn_name.is_some) {
- if (verbose) {
- fprintf(stderr, "%s: Expected function name\n", buf_ptr(glibc_abi->fns_txt_path));
- }
- return ErrorInvalidFormat;
- }
- Optional> opt_lib_name = SplitIterator_next(&line_it);
- if (!opt_lib_name.is_some) {
- if (verbose) {
- fprintf(stderr, "%s: Expected lib name\n", buf_ptr(glibc_abi->fns_txt_path));
- }
- return ErrorInvalidFormat;
- }
-
- Buf *this_fn_name = buf_create_from_slice(opt_fn_name.value);
- Buf *this_lib_name = buf_create_from_slice(opt_lib_name.value);
- glibc_abi->all_functions.append({ this_fn_name, glibc_lib_find(buf_ptr(this_lib_name)) });
- }
- }
- {
- SplitIterator it = memSplit(buf_to_slice(abi_txt_contents), str("\r\n"));
- ZigGLibCVerList *ver_list_base = nullptr;
- int line_num = 0;
- for (;;) {
- if (ver_list_base == nullptr) {
- line_num += 1;
- Optional> opt_line = SplitIterator_next_separate(&it);
- if (!opt_line.is_some) break;
-
- ver_list_base = heap::c_allocator.allocate(glibc_abi->all_functions.length);
- SplitIterator line_it = memSplit(opt_line.value, str(" "));
- for (;;) {
- ZigTarget *target = heap::c_allocator.create();
- Optional> opt_target = SplitIterator_next(&line_it);
- if (!opt_target.is_some) break;
-
- SplitIterator component_it = memSplit(opt_target.value, str("-"));
- Optional> opt_arch = SplitIterator_next(&component_it);
- assert(opt_arch.is_some);
- Optional> opt_os = SplitIterator_next(&component_it);
- assert(opt_os.is_some); // it's always "linux" so we ignore it
- Optional> opt_abi = SplitIterator_next(&component_it);
- assert(opt_abi.is_some);
-
-
- err = target_parse_arch(&target->arch, (char*)opt_arch.value.ptr, opt_arch.value.len);
- assert(err == ErrorNone);
-
- target->os = OsLinux;
-
- err = target_parse_abi(&target->abi, (char*)opt_abi.value.ptr, opt_abi.value.len);
- if (err != ErrorNone) {
- fprintf(stderr, "Error parsing %s:%d: %s\n", buf_ptr(glibc_abi->abi_txt_path),
- line_num, err_str(err));
- fprintf(stderr, "arch: '%.*s', os: '%.*s', abi: '%.*s'\n",
- (int)opt_arch.value.len, (const char*)opt_arch.value.ptr,
- (int)opt_os.value.len, (const char*)opt_os.value.ptr,
- (int)opt_abi.value.len, (const char*)opt_abi.value.ptr);
- fprintf(stderr, "parsed from target: '%.*s'\n",
- (int)opt_target.value.len, (const char*)opt_target.value.ptr);
- fprintf(stderr, "parsed from line:\n%.*s\n", (int)opt_line.value.len, opt_line.value.ptr);
- fprintf(stderr, "Zig installation appears to be corrupted.\n");
- exit(1);
- }
-
- glibc_abi->version_table.put(target, ver_list_base);
- }
- continue;
- }
- for (size_t fn_i = 0; fn_i < glibc_abi->all_functions.length; fn_i += 1) {
- ZigGLibCVerList *ver_list = &ver_list_base[fn_i];
- line_num += 1;
- Optional> opt_line = SplitIterator_next_separate(&it);
- assert(opt_line.is_some);
-
- SplitIterator line_it = memSplit(opt_line.value, str(" "));
- for (;;) {
- Optional> opt_ver = SplitIterator_next(&line_it);
- if (!opt_ver.is_some) break;
- assert(ver_list->len < 8); // increase the array len in the type
-
- unsigned long ver_index = strtoul(buf_ptr(buf_create_from_slice(opt_ver.value)), nullptr, 10);
- assert(ver_index < 255); // use a bigger integer in the type
- ver_list->versions[ver_list->len] = ver_index;
- ver_list->len += 1;
- }
- }
- ver_list_base = nullptr;
- }
- }
-
- *out_result = glibc_abi;
- return ErrorNone;
-}
-
-Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, const ZigTarget *target,
- Buf **out_dir, bool verbose, Stage2ProgressNode *progress_node)
-{
- Error err;
-
- Buf *cache_dir = get_global_cache_dir();
- CacheHash *cache_hash = heap::c_allocator.create();
- Buf *manifest_dir = buf_sprintf("%s" OS_SEP CACHE_HASH_SUBDIR, buf_ptr(cache_dir));
- cache_init(cache_hash, manifest_dir);
-
- Buf *compiler_id;
- if ((err = get_compiler_id(&compiler_id))) {
- if (verbose) {
- fprintf(stderr, "unable to get compiler id: %s\n", err_str(err));
- }
- return err;
- }
- cache_buf(cache_hash, compiler_id);
- cache_int(cache_hash, target->arch);
- cache_int(cache_hash, target->abi);
- cache_int(cache_hash, target->glibc_or_darwin_version->major);
- cache_int(cache_hash, target->glibc_or_darwin_version->minor);
- cache_int(cache_hash, target->glibc_or_darwin_version->patch);
-
- Buf digest = BUF_INIT;
- buf_resize(&digest, 0);
- if ((err = cache_hit(cache_hash, &digest))) {
- // Treat an invalid format error as a cache miss.
- if (err != ErrorInvalidFormat)
- return err;
- }
- // We should always get a cache hit because there are no
- // files in the input hash.
- assert(buf_len(&digest) != 0);
-
- Buf *dummy_dir = buf_alloc();
- os_path_join(manifest_dir, &digest, dummy_dir);
-
- if ((err = os_make_path(dummy_dir)))
- return err;
-
- Buf *test_if_exists_path = buf_alloc();
- os_path_join(dummy_dir, buf_create_from_str("ok"), test_if_exists_path);
-
- bool hit;
- if ((err = os_file_exists(test_if_exists_path, &hit)))
- return err;
-
- if (hit) {
- *out_dir = dummy_dir;
- return ErrorNone;
- }
-
-
- ZigGLibCVerList *ver_list_base = glibc_abi->version_table.get(target);
-
- uint8_t target_ver_index = 0;
- for (;target_ver_index < glibc_abi->all_versions.length; target_ver_index += 1) {
- const Stage2SemVer *this_ver = &glibc_abi->all_versions.at(target_ver_index);
- if (this_ver->major == target->glibc_or_darwin_version->major &&
- this_ver->minor == target->glibc_or_darwin_version->minor &&
- this_ver->patch == target->glibc_or_darwin_version->patch)
- {
- break;
- }
- }
- if (target_ver_index == glibc_abi->all_versions.length) {
- if (verbose) {
- fprintf(stderr, "Unrecognized glibc version: %d.%d.%d\n",
- target->glibc_or_darwin_version->major,
- target->glibc_or_darwin_version->minor,
- target->glibc_or_darwin_version->patch);
- }
- return ErrorUnknownABI;
- }
-
- Buf *map_file_path = buf_sprintf("%s" OS_SEP "all.map", buf_ptr(dummy_dir));
- Buf *map_contents = buf_alloc();
-
- for (uint8_t ver_i = 0; ver_i < glibc_abi->all_versions.length; ver_i += 1) {
- const Stage2SemVer *ver = &glibc_abi->all_versions.at(ver_i);
- if (ver->patch == 0) {
- buf_appendf(map_contents, "GLIBC_%d.%d { };\n", ver->major, ver->minor);
- } else {
- buf_appendf(map_contents, "GLIBC_%d.%d.%d { };\n", ver->major, ver->minor, ver->patch);
- }
- }
-
- if ((err = os_write_file(map_file_path, map_contents))) {
- if (verbose) {
- fprintf(stderr, "unable to write %s: %s", buf_ptr(map_file_path), err_str(err));
- }
- return err;
- }
-
-
- for (size_t lib_i = 0; lib_i < array_length(glibc_libs); lib_i += 1) {
- const ZigGLibCLib *lib = &glibc_libs[lib_i];
- Buf *zig_file_path = buf_sprintf("%s" OS_SEP "%s.zig", buf_ptr(dummy_dir), lib->name);
- Buf *zig_body = buf_alloc();
- Buf *zig_footer = buf_alloc();
-
- buf_appendf(zig_body, "comptime {\n");
- buf_appendf(zig_body, " asm (\n");
-
- for (size_t fn_i = 0; fn_i < glibc_abi->all_functions.length; fn_i += 1) {
- const ZigGLibCFn *libc_fn = &glibc_abi->all_functions.at(fn_i);
- if (libc_fn->lib != lib) continue;
- ZigGLibCVerList *ver_list = &ver_list_base[fn_i];
- // Pick the default symbol version:
- // - If there are no versions, don't emit it
- // - Take the greatest one <= than the target one
- // - If none of them is <= than the
- // specified one don't pick any default version
- if (ver_list->len == 0) continue;
- uint8_t chosen_def_ver_index = 255;
- for (uint8_t ver_i = 0; ver_i < ver_list->len; ver_i += 1) {
- uint8_t ver_index = ver_list->versions[ver_i];
- if ((chosen_def_ver_index == 255 || ver_index > chosen_def_ver_index) &&
- target_ver_index >= ver_index)
- {
- chosen_def_ver_index = ver_index;
- }
- }
- for (uint8_t ver_i = 0; ver_i < ver_list->len; ver_i += 1) {
- uint8_t ver_index = ver_list->versions[ver_i];
-
- Buf *stub_name;
- const Stage2SemVer *ver = &glibc_abi->all_versions.at(ver_index);
- const char *sym_name = buf_ptr(libc_fn->name);
- if (ver->patch == 0) {
- stub_name = buf_sprintf("%s_%d_%d", sym_name, ver->major, ver->minor);
- } else {
- stub_name = buf_sprintf("%s_%d_%d_%d", sym_name, ver->major, ver->minor, ver->patch);
- }
-
- buf_appendf(zig_footer, "export fn %s() void {}\n", buf_ptr(stub_name));
-
- // Default symbol version definition vs normal symbol version definition
- const char *at_sign_str = (chosen_def_ver_index != 255 &&
- ver_index == chosen_def_ver_index) ? "@@" : "@";
- if (ver->patch == 0) {
- buf_appendf(zig_body, " \\\\ .symver %s, %s%sGLIBC_%d.%d\n",
- buf_ptr(stub_name), sym_name, at_sign_str, ver->major, ver->minor);
- } else {
- buf_appendf(zig_body, " \\\\ .symver %s, %s%sGLIBC_%d.%d.%d\n",
- buf_ptr(stub_name), sym_name, at_sign_str, ver->major, ver->minor, ver->patch);
- }
- // Hide the stub to keep the symbol table clean
- buf_appendf(zig_body, " \\\\ .hidden %s\n", buf_ptr(stub_name));
- }
- }
-
- buf_appendf(zig_body, " );\n");
- buf_appendf(zig_body, "}\n");
- buf_append_buf(zig_body, zig_footer);
-
- if ((err = os_write_file(zig_file_path, zig_body))) {
- if (verbose) {
- fprintf(stderr, "unable to write %s: %s", buf_ptr(zig_file_path), err_str(err));
- }
- return err;
- }
-
- bool is_ld = (strcmp(lib->name, "ld") == 0);
-
- CodeGen *child_gen = create_child_codegen(g, zig_file_path, OutTypeLib, nullptr, lib->name, progress_node);
- codegen_set_lib_version(child_gen, true, lib->sover, 0, 0);
- child_gen->is_dynamic = true;
- child_gen->is_dummy_so = true;
- child_gen->version_script_path = map_file_path;
- child_gen->enable_cache = false;
- child_gen->output_dir = dummy_dir;
- if (is_ld) {
- assert(g->zig_target->standard_dynamic_linker_path != nullptr);
- Buf *ld_basename = buf_alloc();
- os_path_split(buf_create_from_str(g->zig_target->standard_dynamic_linker_path),
- nullptr, ld_basename);
- child_gen->override_soname = ld_basename;
- }
- codegen_build_and_link(child_gen);
- }
-
- if ((err = os_write_file(test_if_exists_path, buf_alloc()))) {
- if (verbose) {
- fprintf(stderr, "unable to write %s: %s", buf_ptr(test_if_exists_path), err_str(err));
- }
- return err;
- }
- *out_dir = dummy_dir;
- return ErrorNone;
-}
-
-uint32_t hash_glibc_target(const ZigTarget *x) {
- return x->arch * (uint32_t)3250106448 +
- x->os * (uint32_t)542534372 +
- x->abi * (uint32_t)59162639;
-}
-
-bool eql_glibc_target(const ZigTarget *a, const ZigTarget *b) {
- return a->arch == b->arch &&
- a->os == b->os &&
- a->abi == b->abi;
-}
-
-size_t glibc_lib_count(void) {
- return array_length(glibc_libs);
-}
-
-const ZigGLibCLib *glibc_lib_enum(size_t index) {
- assert(index < array_length(glibc_libs));
- return &glibc_libs[index];
-}
-
-const ZigGLibCLib *glibc_lib_find(const char *name) {
- for (size_t i = 0; i < array_length(glibc_libs); i += 1) {
- if (strcmp(glibc_libs[i].name, name) == 0) {
- return &glibc_libs[i];
- }
- }
- return nullptr;
-}
diff --git a/src/glibc.hpp b/src/glibc.hpp
deleted file mode 100644
index c04dcb4629..0000000000
--- a/src/glibc.hpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (c) 2019 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#ifndef ZIG_GLIBC_HPP
-#define ZIG_GLIBC_HPP
-
-#include "all_types.hpp"
-
-struct ZigGLibCLib {
- const char *name;
- uint8_t sover;
-};
-
-struct ZigGLibCFn {
- Buf *name;
- const ZigGLibCLib *lib;
-};
-
-struct ZigGLibCVerList {
- uint8_t versions[8]; // 8 is just the max number, we know statically it's big enough
- uint8_t len;
-};
-
-uint32_t hash_glibc_target(const ZigTarget *x);
-bool eql_glibc_target(const ZigTarget *a, const ZigTarget *b);
-
-struct ZigGLibCAbi {
- Buf *abi_txt_path;
- Buf *vers_txt_path;
- Buf *fns_txt_path;
- ZigList all_versions;
- ZigList all_functions;
- // The value is a pointer to all_functions.length items and each item is an index
- // into all_functions.
- HashMap version_table;
-};
-
-Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbose);
-Error glibc_build_dummies_and_maps(CodeGen *codegen, const ZigGLibCAbi *glibc_abi, const ZigTarget *target,
- Buf **out_dir, bool verbose, Stage2ProgressNode *progress_node);
-
-size_t glibc_lib_count(void);
-const ZigGLibCLib *glibc_lib_enum(size_t index);
-const ZigGLibCLib *glibc_lib_find(const char *name);
-
-#endif
diff --git a/src/glibc.zig b/src/glibc.zig
new file mode 100644
index 0000000000..1860726f93
--- /dev/null
+++ b/src/glibc.zig
@@ -0,0 +1,956 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const mem = std.mem;
+const path = std.fs.path;
+const assert = std.debug.assert;
+
+const target_util = @import("target.zig");
+const Compilation = @import("Compilation.zig");
+const build_options = @import("build_options");
+const trace = @import("tracy.zig").trace;
+const Cache = @import("Cache.zig");
+const Package = @import("Package.zig");
+
+pub const Lib = struct {
+ name: []const u8,
+ sover: u8,
+};
+
+pub const Fn = struct {
+ name: []const u8,
+ lib: *const Lib,
+};
+
+pub const VerList = struct {
+ /// 7 is just the max number, we know statically it's big enough.
+ versions: [7]u8,
+ len: u8,
+};
+
+pub const ABI = struct {
+ all_versions: []const std.builtin.Version,
+ all_functions: []const Fn,
+ /// The value is a pointer to all_functions.len items and each item is an index into all_functions.
+ version_table: std.AutoHashMapUnmanaged(target_util.ArchOsAbi, [*]VerList),
+ arena_state: std.heap.ArenaAllocator.State,
+
+ pub fn destroy(abi: *ABI, gpa: *Allocator) void {
+ abi.version_table.deinit(gpa);
+ abi.arena_state.promote(gpa).deinit(); // Frees the ABI memory too.
+ }
+};
+
+pub const libs = [_]Lib{
+ .{ .name = "c", .sover = 6 },
+ .{ .name = "m", .sover = 6 },
+ .{ .name = "pthread", .sover = 0 },
+ .{ .name = "dl", .sover = 2 },
+ .{ .name = "rt", .sover = 1 },
+ .{ .name = "ld", .sover = 2 },
+ .{ .name = "util", .sover = 1 },
+};
+
+pub const LoadMetaDataError = error{
+ /// The files that ship with the Zig compiler were unable to be read, or otherwise had malformed data.
+ ZigInstallationCorrupt,
+ OutOfMemory,
+};
+
+/// This function will emit a log error when there is a problem with the zig installation and then return
+/// `error.ZigInstallationCorrupt`.
+pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError!*ABI {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ var arena_allocator = std.heap.ArenaAllocator.init(gpa);
+ errdefer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ var all_versions = std.ArrayListUnmanaged(std.builtin.Version){};
+ var all_functions = std.ArrayListUnmanaged(Fn){};
+ var version_table = std.AutoHashMapUnmanaged(target_util.ArchOsAbi, [*]VerList){};
+ errdefer version_table.deinit(gpa);
+
+ var glibc_dir = zig_lib_dir.openDir("libc" ++ path.sep_str ++ "glibc", .{}) catch |err| {
+ std.log.err("unable to open glibc dir: {}", .{@errorName(err)});
+ return error.ZigInstallationCorrupt;
+ };
+ defer glibc_dir.close();
+
+ const max_txt_size = 500 * 1024; // Bigger than this and something is definitely borked.
+ const vers_txt_contents = glibc_dir.readFileAlloc(gpa, "vers.txt", max_txt_size) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ else => {
+ std.log.err("unable to read vers.txt: {}", .{@errorName(err)});
+ return error.ZigInstallationCorrupt;
+ },
+ };
+ defer gpa.free(vers_txt_contents);
+
+ // Arena allocated because the result contains references to function names.
+ const fns_txt_contents = glibc_dir.readFileAlloc(arena, "fns.txt", max_txt_size) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ else => {
+ std.log.err("unable to read fns.txt: {}", .{@errorName(err)});
+ return error.ZigInstallationCorrupt;
+ },
+ };
+
+ const abi_txt_contents = glibc_dir.readFileAlloc(gpa, "abi.txt", max_txt_size) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ else => {
+ std.log.err("unable to read abi.txt: {}", .{@errorName(err)});
+ return error.ZigInstallationCorrupt;
+ },
+ };
+ defer gpa.free(abi_txt_contents);
+
+ {
+ var it = mem.tokenize(vers_txt_contents, "\r\n");
+ var line_i: usize = 1;
+ while (it.next()) |line| : (line_i += 1) {
+ const prefix = "GLIBC_";
+ if (!mem.startsWith(u8, line, prefix)) {
+ std.log.err("vers.txt:{}: expected 'GLIBC_' prefix", .{line_i});
+ return error.ZigInstallationCorrupt;
+ }
+ const adjusted_line = line[prefix.len..];
+ const ver = std.builtin.Version.parse(adjusted_line) catch |err| {
+ std.log.err("vers.txt:{}: unable to parse glibc version '{}': {}", .{ line_i, line, @errorName(err) });
+ return error.ZigInstallationCorrupt;
+ };
+ try all_versions.append(arena, ver);
+ }
+ }
+ {
+ var file_it = mem.tokenize(fns_txt_contents, "\r\n");
+ var line_i: usize = 1;
+ while (file_it.next()) |line| : (line_i += 1) {
+ var line_it = mem.tokenize(line, " ");
+ const fn_name = line_it.next() orelse {
+ std.log.err("fns.txt:{}: expected function name", .{line_i});
+ return error.ZigInstallationCorrupt;
+ };
+ const lib_name = line_it.next() orelse {
+ std.log.err("fns.txt:{}: expected library name", .{line_i});
+ return error.ZigInstallationCorrupt;
+ };
+ const lib = findLib(lib_name) orelse {
+ std.log.err("fns.txt:{}: unknown library name: {}", .{ line_i, lib_name });
+ return error.ZigInstallationCorrupt;
+ };
+ try all_functions.append(arena, .{
+ .name = fn_name,
+ .lib = lib,
+ });
+ }
+ }
+ {
+ var file_it = mem.split(abi_txt_contents, "\n");
+ var line_i: usize = 0;
+ while (true) {
+ const ver_list_base: []VerList = blk: {
+ const line = file_it.next() orelse break;
+ if (line.len == 0) break;
+ line_i += 1;
+ const ver_list_base = try arena.alloc(VerList, all_functions.items.len);
+ var line_it = mem.tokenize(line, " ");
+ while (line_it.next()) |target_string| {
+ var component_it = mem.tokenize(target_string, "-");
+ const arch_name = component_it.next() orelse {
+ std.log.err("abi.txt:{}: expected arch name", .{line_i});
+ return error.ZigInstallationCorrupt;
+ };
+ const os_name = component_it.next() orelse {
+ std.log.err("abi.txt:{}: expected OS name", .{line_i});
+ return error.ZigInstallationCorrupt;
+ };
+ const abi_name = component_it.next() orelse {
+ std.log.err("abi.txt:{}: expected ABI name", .{line_i});
+ return error.ZigInstallationCorrupt;
+ };
+ const arch_tag = std.meta.stringToEnum(std.Target.Cpu.Arch, arch_name) orelse {
+ std.log.err("abi.txt:{}: unrecognized arch: '{}'", .{ line_i, arch_name });
+ return error.ZigInstallationCorrupt;
+ };
+ if (!mem.eql(u8, os_name, "linux")) {
+ std.log.err("abi.txt:{}: expected OS 'linux', found '{}'", .{ line_i, os_name });
+ return error.ZigInstallationCorrupt;
+ }
+ const abi_tag = std.meta.stringToEnum(std.Target.Abi, abi_name) orelse {
+ std.log.err("abi.txt:{}: unrecognized ABI: '{}'", .{ line_i, abi_name });
+ return error.ZigInstallationCorrupt;
+ };
+
+ const triple = target_util.ArchOsAbi{
+ .arch = arch_tag,
+ .os = .linux,
+ .abi = abi_tag,
+ };
+ try version_table.put(gpa, triple, ver_list_base.ptr);
+ }
+ break :blk ver_list_base;
+ };
+ for (ver_list_base) |*ver_list| {
+ const line = file_it.next() orelse {
+ std.log.err("abi.txt:{}: missing version number line", .{line_i});
+ return error.ZigInstallationCorrupt;
+ };
+ line_i += 1;
+
+ ver_list.* = .{
+ .versions = undefined,
+ .len = 0,
+ };
+ var line_it = mem.tokenize(line, " ");
+ while (line_it.next()) |version_index_string| {
+ if (ver_list.len >= ver_list.versions.len) {
+ // If this happens with legit data, increase the array len in the type.
+ std.log.err("abi.txt:{}: too many versions", .{line_i});
+ return error.ZigInstallationCorrupt;
+ }
+ const version_index = std.fmt.parseInt(u8, version_index_string, 10) catch |err| {
+ // If this happens with legit data, increase the size of the integer type in the struct.
+ std.log.err("abi.txt:{}: unable to parse version: {}", .{ line_i, @errorName(err) });
+ return error.ZigInstallationCorrupt;
+ };
+
+ ver_list.versions[ver_list.len] = version_index;
+ ver_list.len += 1;
+ }
+ }
+ }
+ }
+
+ const abi = try arena.create(ABI);
+ abi.* = .{
+ .all_versions = all_versions.items,
+ .all_functions = all_functions.items,
+ .version_table = version_table,
+ .arena_state = arena_allocator.state,
+ };
+ return abi;
+}
+
+fn findLib(name: []const u8) ?*const Lib {
+ for (libs) |*lib| {
+ if (mem.eql(u8, lib.name, name)) {
+ return lib;
+ }
+ }
+ return null;
+}
+
+pub const CRTFile = enum {
+ crti_o,
+ crtn_o,
+ scrt1_o,
+ libc_nonshared_a,
+};
+
+pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
+ if (!build_options.have_llvm) {
+ return error.ZigCompilerNotBuiltWithLLVMExtensions;
+ }
+ const gpa = comp.gpa;
+ var arena_allocator = std.heap.ArenaAllocator.init(gpa);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ switch (crt_file) {
+ .crti_o => {
+ var args = std.ArrayList([]const u8).init(arena);
+ try add_include_dirs(comp, arena, &args);
+ try args.appendSlice(&[_][]const u8{
+ "-D_LIBC_REENTRANT",
+ "-include",
+ try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"),
+ "-DMODULE_NAME=libc",
+ "-Wno-nonportable-include-path",
+ "-include",
+ try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"),
+ "-DTOP_NAMESPACE=glibc",
+ "-DASSEMBLER",
+ "-g",
+ "-Wa,--noexecstack",
+ });
+ return comp.build_crt_file("crti", .Obj, &[1]Compilation.CSourceFile{
+ .{
+ .src_path = try start_asm_path(comp, arena, "crti.S"),
+ .extra_flags = args.items,
+ },
+ });
+ },
+ .crtn_o => {
+ var args = std.ArrayList([]const u8).init(arena);
+ try add_include_dirs(comp, arena, &args);
+ try args.appendSlice(&[_][]const u8{
+ "-D_LIBC_REENTRANT",
+ "-DMODULE_NAME=libc",
+ "-DTOP_NAMESPACE=glibc",
+ "-DASSEMBLER",
+ "-g",
+ "-Wa,--noexecstack",
+ });
+ return comp.build_crt_file("crtn", .Obj, &[1]Compilation.CSourceFile{
+ .{
+ .src_path = try start_asm_path(comp, arena, "crtn.S"),
+ .extra_flags = args.items,
+ },
+ });
+ },
+ .scrt1_o => {
+ const start_os: Compilation.CSourceFile = blk: {
+ var args = std.ArrayList([]const u8).init(arena);
+ try add_include_dirs(comp, arena, &args);
+ try args.appendSlice(&[_][]const u8{
+ "-D_LIBC_REENTRANT",
+ "-include",
+ try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"),
+ "-DMODULE_NAME=libc",
+ "-Wno-nonportable-include-path",
+ "-include",
+ try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"),
+ "-DPIC",
+ "-DSHARED",
+ "-DTOP_NAMESPACE=glibc",
+ "-DASSEMBLER",
+ "-g",
+ "-Wa,--noexecstack",
+ });
+ break :blk .{
+ .src_path = try start_asm_path(comp, arena, "start.S"),
+ .extra_flags = args.items,
+ };
+ };
+ const abi_note_o: Compilation.CSourceFile = blk: {
+ var args = std.ArrayList([]const u8).init(arena);
+ try args.appendSlice(&[_][]const u8{
+ "-I",
+ try lib_path(comp, arena, lib_libc_glibc ++ "csu"),
+ });
+ try add_include_dirs(comp, arena, &args);
+ try args.appendSlice(&[_][]const u8{
+ "-D_LIBC_REENTRANT",
+ "-DMODULE_NAME=libc",
+ "-DTOP_NAMESPACE=glibc",
+ "-DASSEMBLER",
+ "-g",
+ "-Wa,--noexecstack",
+ });
+ break :blk .{
+ .src_path = try lib_path(comp, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "abi-note.S"),
+ .extra_flags = args.items,
+ };
+ };
+ return comp.build_crt_file("Scrt1", .Obj, &[_]Compilation.CSourceFile{ start_os, abi_note_o });
+ },
+ .libc_nonshared_a => {
+ const deps = [_][]const u8{
+ lib_libc_glibc ++ "stdlib" ++ path.sep_str ++ "atexit.c",
+ lib_libc_glibc ++ "stdlib" ++ path.sep_str ++ "at_quick_exit.c",
+ lib_libc_glibc ++ "io" ++ path.sep_str ++ "stat.c",
+ lib_libc_glibc ++ "io" ++ path.sep_str ++ "fstat.c",
+ lib_libc_glibc ++ "io" ++ path.sep_str ++ "lstat.c",
+ lib_libc_glibc ++ "io" ++ path.sep_str ++ "stat64.c",
+ lib_libc_glibc ++ "io" ++ path.sep_str ++ "fstat64.c",
+ lib_libc_glibc ++ "io" ++ path.sep_str ++ "lstat64.c",
+ lib_libc_glibc ++ "io" ++ path.sep_str ++ "fstatat.c",
+ lib_libc_glibc ++ "io" ++ path.sep_str ++ "fstatat64.c",
+ lib_libc_glibc ++ "io" ++ path.sep_str ++ "mknod.c",
+ lib_libc_glibc ++ "io" ++ path.sep_str ++ "mknodat.c",
+ lib_libc_glibc ++ "nptl" ++ path.sep_str ++ "pthread_atfork.c",
+ lib_libc_glibc ++ "debug" ++ path.sep_str ++ "stack_chk_fail_local.c",
+ };
+
+ var c_source_files: [deps.len + 1]Compilation.CSourceFile = undefined;
+
+ c_source_files[0] = blk: {
+ var args = std.ArrayList([]const u8).init(arena);
+ try args.appendSlice(&[_][]const u8{
+ "-std=gnu11",
+ "-fgnu89-inline",
+ "-g",
+ "-O2",
+ "-fmerge-all-constants",
+ "-fno-stack-protector",
+ "-fmath-errno",
+ "-fno-stack-protector",
+ "-I",
+ try lib_path(comp, arena, lib_libc_glibc ++ "csu"),
+ });
+ try add_include_dirs(comp, arena, &args);
+ try args.appendSlice(&[_][]const u8{
+ "-DSTACK_PROTECTOR_LEVEL=0",
+ "-fPIC",
+ "-fno-stack-protector",
+ "-ftls-model=initial-exec",
+ "-D_LIBC_REENTRANT",
+ "-include",
+ try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"),
+ "-DMODULE_NAME=libc",
+ "-Wno-nonportable-include-path",
+ "-include",
+ try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"),
+ "-DPIC",
+ "-DLIBC_NONSHARED=1",
+ "-DTOP_NAMESPACE=glibc",
+ });
+ break :blk .{
+ .src_path = try lib_path(comp, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "elf-init.c"),
+ .extra_flags = args.items,
+ };
+ };
+
+ for (deps) |dep, i| {
+ var args = std.ArrayList([]const u8).init(arena);
+ try args.appendSlice(&[_][]const u8{
+ "-std=gnu11",
+ "-fgnu89-inline",
+ "-g",
+ "-O2",
+ "-fmerge-all-constants",
+ "-fno-stack-protector",
+ "-fmath-errno",
+ "-ftls-model=initial-exec",
+ "-Wno-ignored-attributes",
+ });
+ try add_include_dirs(comp, arena, &args);
+ try args.appendSlice(&[_][]const u8{
+ "-D_LIBC_REENTRANT",
+ "-include",
+ try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"),
+ "-DMODULE_NAME=libc",
+ "-Wno-nonportable-include-path",
+ "-include",
+ try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"),
+ "-DPIC",
+ "-DLIBC_NONSHARED=1",
+ "-DTOP_NAMESPACE=glibc",
+ });
+ c_source_files[i + 1] = .{
+ .src_path = try lib_path(comp, arena, dep),
+ .extra_flags = args.items,
+ };
+ }
+ return comp.build_crt_file("c_nonshared", .Lib, &c_source_files);
+ },
+ }
+}
+
+fn start_asm_path(comp: *Compilation, arena: *Allocator, basename: []const u8) ![]const u8 {
+ const arch = comp.getTarget().cpu.arch;
+ const is_ppc = arch == .powerpc or arch == .powerpc64 or arch == .powerpc64le;
+ const is_aarch64 = arch == .aarch64 or arch == .aarch64_be;
+ const is_sparc = arch == .sparc or arch == .sparcel or arch == .sparcv9;
+ const is_64 = arch.ptrBitWidth() == 64;
+
+ const s = path.sep_str;
+
+ var result = std.ArrayList(u8).init(arena);
+ try result.appendSlice(comp.zig_lib_directory.path.?);
+ try result.appendSlice(s ++ "libc" ++ s ++ "glibc" ++ s ++ "sysdeps" ++ s);
+ if (is_sparc) {
+ if (is_64) {
+ try result.appendSlice("sparc" ++ s ++ "sparc64");
+ } else {
+ try result.appendSlice("sparc" ++ s ++ "sparc32");
+ }
+ } else if (arch.isARM()) {
+ try result.appendSlice("arm");
+ } else if (arch.isMIPS()) {
+ try result.appendSlice("mips");
+ } else if (arch == .x86_64) {
+ try result.appendSlice("x86_64");
+ } else if (arch == .i386) {
+ try result.appendSlice("i386");
+ } else if (is_aarch64) {
+ try result.appendSlice("aarch64");
+ } else if (arch.isRISCV()) {
+ try result.appendSlice("riscv");
+ } else if (is_ppc) {
+ if (is_64) {
+ try result.appendSlice("powerpc" ++ s ++ "powerpc64");
+ } else {
+ try result.appendSlice("powerpc" ++ s ++ "powerpc32");
+ }
+ }
+
+ try result.appendSlice(s);
+ try result.appendSlice(basename);
+ return result.items;
+}
+
+fn add_include_dirs(comp: *Compilation, arena: *Allocator, args: *std.ArrayList([]const u8)) error{OutOfMemory}!void {
+ const target = comp.getTarget();
+ const arch = target.cpu.arch;
+ const opt_nptl: ?[]const u8 = if (target.os.tag == .linux) "nptl" else "htl";
+ const glibc = try lib_path(comp, arena, lib_libc ++ "glibc");
+
+ const s = path.sep_str;
+
+ try args.append("-I");
+ try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "include"));
+
+ if (target.os.tag == .linux) {
+ try add_include_dirs_arch(arena, args, arch, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix" ++ s ++ "sysv" ++ s ++ "linux"));
+ }
+
+ if (opt_nptl) |nptl| {
+ try add_include_dirs_arch(arena, args, arch, nptl, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps"));
+ }
+
+ if (target.os.tag == .linux) {
+ try args.append("-I");
+ try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++
+ "unix" ++ s ++ "sysv" ++ s ++ "linux" ++ s ++ "generic"));
+
+ try args.append("-I");
+ try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++
+ "unix" ++ s ++ "sysv" ++ s ++ "linux" ++ s ++ "include"));
+ try args.append("-I");
+ try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++
+ "unix" ++ s ++ "sysv" ++ s ++ "linux"));
+ }
+ if (opt_nptl) |nptl| {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, lib_libc_glibc ++ "sysdeps", nptl }));
+ }
+
+ try args.append("-I");
+ try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "pthread"));
+
+ try args.append("-I");
+ try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix" ++ s ++ "sysv"));
+
+ try add_include_dirs_arch(arena, args, arch, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix"));
+
+ try args.append("-I");
+ try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix"));
+
+ try add_include_dirs_arch(arena, args, arch, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps"));
+
+ try args.append("-I");
+ try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "generic"));
+
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, lib_libc ++ "glibc" }));
+
+ try args.append("-I");
+ try args.append(try std.fmt.allocPrint(arena, "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-{}", .{
+ comp.zig_lib_directory.path.?, @tagName(arch), @tagName(target.os.tag), @tagName(target.abi),
+ }));
+
+ try args.append("-I");
+ try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "generic-glibc"));
+
+ try args.append("-I");
+ try args.append(try std.fmt.allocPrint(arena, "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-linux-any", .{
+ comp.zig_lib_directory.path.?, @tagName(arch),
+ }));
+
+ try args.append("-I");
+ try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "any-linux-any"));
+}
+
+fn add_include_dirs_arch(
+ arena: *Allocator,
+ args: *std.ArrayList([]const u8),
+ arch: std.Target.Cpu.Arch,
+ opt_nptl: ?[]const u8,
+ dir: []const u8,
+) error{OutOfMemory}!void {
+ const is_x86 = arch == .i386 or arch == .x86_64;
+ const is_aarch64 = arch == .aarch64 or arch == .aarch64_be;
+ const is_ppc = arch == .powerpc or arch == .powerpc64 or arch == .powerpc64le;
+ const is_sparc = arch == .sparc or arch == .sparcel or arch == .sparcv9;
+ const is_64 = arch.ptrBitWidth() == 64;
+
+ const s = path.sep_str;
+
+ if (is_x86) {
+ if (arch == .x86_64) {
+ if (opt_nptl) |nptl| {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "x86_64", nptl }));
+ } else {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "x86_64" }));
+ }
+ } else if (arch == .i386) {
+ if (opt_nptl) |nptl| {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "i386", nptl }));
+ } else {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "i386" }));
+ }
+ }
+ if (opt_nptl) |nptl| {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "x86", nptl }));
+ } else {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "x86" }));
+ }
+ } else if (arch.isARM()) {
+ if (opt_nptl) |nptl| {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "arm", nptl }));
+ } else {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "arm" }));
+ }
+ } else if (arch.isMIPS()) {
+ if (opt_nptl) |nptl| {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "mips", nptl }));
+ } else {
+ if (is_64) {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" ++ s ++ "mips64" }));
+ } else {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" ++ s ++ "mips32" }));
+ }
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" }));
+ }
+ } else if (is_sparc) {
+ if (opt_nptl) |nptl| {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc", nptl }));
+ } else {
+ if (is_64) {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" ++ s ++ "sparc64" }));
+ } else {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" ++ s ++ "sparc32" }));
+ }
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" }));
+ }
+ } else if (is_aarch64) {
+ if (opt_nptl) |nptl| {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "aarch64", nptl }));
+ } else {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "aarch64" }));
+ }
+ } else if (is_ppc) {
+ if (opt_nptl) |nptl| {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc", nptl }));
+ } else {
+ if (is_64) {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" ++ s ++ "powerpc64" }));
+ } else {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" ++ s ++ "powerpc32" }));
+ }
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" }));
+ }
+ } else if (arch.isRISCV()) {
+ if (opt_nptl) |nptl| {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "riscv", nptl }));
+ } else {
+ try args.append("-I");
+ try args.append(try path.join(arena, &[_][]const u8{ dir, "riscv" }));
+ }
+ }
+}
+
+fn path_from_lib(comp: *Compilation, arena: *Allocator, sub_path: []const u8) ![]const u8 {
+ return path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, sub_path });
+}
+
+const lib_libc = "libc" ++ path.sep_str;
+const lib_libc_glibc = lib_libc ++ "glibc" ++ path.sep_str;
+
+fn lib_path(comp: *Compilation, arena: *Allocator, sub_path: []const u8) ![]const u8 {
+ return path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, sub_path });
+}
+
+pub const BuiltSharedObjects = struct {
+ lock: Cache.Lock,
+ dir_path: []u8,
+
+ pub fn deinit(self: *BuiltSharedObjects, gpa: *Allocator) void {
+ self.lock.release();
+ gpa.free(self.dir_path);
+ self.* = undefined;
+ }
+};
+
+const all_map_basename = "all.map";
+
+// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
+// zig fmt: off
+
+pub fn buildSharedObjects(comp: *Compilation) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ if (!build_options.have_llvm) {
+ return error.ZigCompilerNotBuiltWithLLVMExtensions;
+ }
+
+ var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ const target = comp.getTarget();
+ const target_version = target.os.version_range.linux.glibc;
+
+ // Use the global cache directory.
+ var cache_parent: Cache = .{
+ .gpa = comp.gpa,
+ .manifest_dir = try comp.global_cache_directory.handle.makeOpenPath("h", .{}),
+ };
+ defer cache_parent.manifest_dir.close();
+
+ var cache = cache_parent.obtain();
+ defer cache.deinit();
+ cache.hash.addBytes(build_options.version);
+ cache.hash.addBytes(comp.zig_lib_directory.path orelse ".");
+ cache.hash.add(target.cpu.arch);
+ cache.hash.add(target.abi);
+ cache.hash.add(target_version);
+
+ const hit = try cache.hit();
+ const digest = cache.final();
+ const o_sub_path = try path.join(arena, &[_][]const u8{ "o", &digest });
+
+ // Even if we get a hit, it doesn't guarantee that we finished the job last time.
+ // We use the presence of an "ok" file to determine if it is a true hit.
+
+ var o_directory: Compilation.Directory = .{
+ .handle = try comp.global_cache_directory.handle.makeOpenPath(o_sub_path, .{}),
+ .path = try path.join(arena, &[_][]const u8{ comp.global_cache_directory.path.?, o_sub_path }),
+ };
+ defer o_directory.handle.close();
+
+ const ok_basename = "ok";
+ const actual_hit = if (hit) blk: {
+ o_directory.handle.access(ok_basename, .{}) catch |err| switch (err) {
+ error.FileNotFound => break :blk false,
+ else => |e| return e,
+ };
+ break :blk true;
+ } else false;
+
+ if (!actual_hit) {
+ const metadata = try loadMetaData(comp.gpa, comp.zig_lib_directory.handle);
+ defer metadata.destroy(comp.gpa);
+
+ const ver_list_base = metadata.version_table.get(.{
+ .arch = target.cpu.arch,
+ .os = target.os.tag,
+ .abi = target.abi,
+ }) orelse return error.GLibCUnavailableForThisTarget;
+ const target_ver_index = for (metadata.all_versions) |ver, i| {
+ switch (ver.order(target_version)) {
+ .eq => break i,
+ .lt => continue,
+ .gt => {
+ // TODO Expose via compile error mechanism instead of log.
+ std.log.warn("invalid target glibc version: {}", .{target_version});
+ return error.InvalidTargetGLibCVersion;
+ },
+ }
+ } else blk: {
+ const latest_index = metadata.all_versions.len - 1;
+ std.log.warn("zig cannot build new glibc version {}; providing instead {}", .{
+ target_version, metadata.all_versions[latest_index],
+ });
+ break :blk latest_index;
+ };
+ {
+ var map_contents = std.ArrayList(u8).init(arena);
+ for (metadata.all_versions) |ver| {
+ if (ver.patch == 0) {
+ try map_contents.writer().print("GLIBC_{d}.{d} {{ }};\n", .{ ver.major, ver.minor });
+ } else {
+ try map_contents.writer().print("GLIBC_{d}.{d}.{d} {{ }};\n", .{ ver.major, ver.minor, ver.patch });
+ }
+ }
+ try o_directory.handle.writeFile(all_map_basename, map_contents.items);
+ map_contents.deinit(); // The most recent allocation of an arena can be freed :)
+ }
+ var zig_body = std.ArrayList(u8).init(comp.gpa);
+ defer zig_body.deinit();
+ for (libs) |*lib| {
+ zig_body.shrinkRetainingCapacity(0);
+
+ for (metadata.all_functions) |*libc_fn, fn_i| {
+ if (libc_fn.lib != lib) continue;
+
+ const ver_list = ver_list_base[fn_i];
+ // Pick the default symbol version:
+ // - If there are no versions, don't emit it
+ // - Take the greatest one <= than the target one
+ // - If none of them is <= than the
+ // specified one don't pick any default version
+ if (ver_list.len == 0) continue;
+ var chosen_def_ver_index: u8 = 255;
+ {
+ var ver_i: u8 = 0;
+ while (ver_i < ver_list.len) : (ver_i += 1) {
+ const ver_index = ver_list.versions[ver_i];
+ if ((chosen_def_ver_index == 255 or ver_index > chosen_def_ver_index) and
+ target_ver_index >= ver_index)
+ {
+ chosen_def_ver_index = ver_index;
+ }
+ }
+ }
+ {
+ var ver_i: u8 = 0;
+ while (ver_i < ver_list.len) : (ver_i += 1) {
+ // Example:
+ // .globl _Exit_2_2_5
+ // .type _Exit_2_2_5, @function;
+ // .symver _Exit_2_2_5, _Exit@@GLIBC_2.2.5
+ // .hidden _Exit_2_2_5
+ // _Exit_2_2_5:
+ const ver_index = ver_list.versions[ver_i];
+ const ver = metadata.all_versions[ver_index];
+ const sym_name = libc_fn.name;
+ // Default symbol version definition vs normal symbol version definition
+ const want_two_ats = chosen_def_ver_index != 255 and ver_index == chosen_def_ver_index;
+ const at_sign_str = "@@"[0 .. @boolToInt(want_two_ats) + @as(usize, 1)];
+
+ if (ver.patch == 0) {
+ const sym_plus_ver = try std.fmt.allocPrint(
+ arena, "{s}_{d}_{d}",
+ .{sym_name, ver.major, ver.minor},
+ );
+ try zig_body.writer().print(
+ \\.globl {s}
+ \\.type {s}, @function;
+ \\.symver {s}, {s}{s}GLIBC_{d}.{d}
+ \\.hidden {s}
+ \\{s}:
+ \\
+ , .{
+ sym_plus_ver,
+ sym_plus_ver,
+ sym_plus_ver, sym_name, at_sign_str, ver.major, ver.minor,
+ sym_plus_ver,
+ sym_plus_ver,
+ });
+ } else {
+ const sym_plus_ver = try std.fmt.allocPrint(arena, "{s}_{d}_{d}_{d}",
+ .{sym_name, ver.major, ver.minor, ver.patch},
+ );
+ try zig_body.writer().print(
+ \\.globl {s}
+ \\.type {s}, @function;
+ \\.symver {s}, {s}{s}GLIBC_{d}.{d}.{d}
+ \\.hidden {s}
+ \\{s}:
+ \\
+ , .{
+ sym_plus_ver,
+ sym_plus_ver,
+ sym_plus_ver, sym_name, at_sign_str, ver.major, ver.minor, ver.patch,
+ sym_plus_ver,
+ sym_plus_ver,
+ });
+ }
+ }
+ }
+ }
+
+ var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "pthread", etc.
+ const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable;
+ try o_directory.handle.writeFile(asm_file_basename, zig_body.items);
+
+ try buildSharedLib(comp, arena, comp.global_cache_directory, o_directory, asm_file_basename, lib);
+ }
+ // No need to write the manifest because there are no file inputs associated with this cache hash.
+ // However we do need to write the ok file now.
+ if (o_directory.handle.createFile(ok_basename, .{})) |file| {
+ file.close();
+ } else |err| {
+ std.log.warn("glibc shared objects: failed to mark completion: {}", .{@errorName(err)});
+ }
+ }
+
+ assert(comp.glibc_so_files == null);
+ comp.glibc_so_files = BuiltSharedObjects{
+ .lock = cache.toOwnedLock(),
+ .dir_path = try path.join(comp.gpa, &[_][]const u8{ comp.global_cache_directory.path.?, o_sub_path }),
+ };
+}
+
+// zig fmt: on
+
+fn buildSharedLib(
+ comp: *Compilation,
+ arena: *Allocator,
+ zig_cache_directory: Compilation.Directory,
+ bin_directory: Compilation.Directory,
+ asm_file_basename: []const u8,
+ lib: *const Lib,
+) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const emit_bin = Compilation.EmitLoc{
+ .directory = bin_directory,
+ .basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover }),
+ };
+ const version: std.builtin.Version = .{ .major = lib.sover, .minor = 0, .patch = 0 };
+ const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?);
+ const override_soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else null;
+ const map_file_path = try path.join(arena, &[_][]const u8{ bin_directory.path.?, all_map_basename });
+ const c_source_files = [1]Compilation.CSourceFile{
+ .{
+ .src_path = try path.join(arena, &[_][]const u8{ bin_directory.path.?, asm_file_basename }),
+ },
+ };
+ const sub_compilation = try Compilation.create(comp.gpa, .{
+ .local_cache_directory = zig_cache_directory,
+ .global_cache_directory = comp.global_cache_directory,
+ .zig_lib_directory = comp.zig_lib_directory,
+ .target = comp.getTarget(),
+ .root_name = lib.name,
+ .root_pkg = null,
+ .output_mode = .Lib,
+ .link_mode = .Dynamic,
+ .rand = comp.rand,
+ .libc_installation = comp.bin_file.options.libc_installation,
+ .emit_bin = emit_bin,
+ .optimize_mode = comp.bin_file.options.optimize_mode,
+ .want_sanitize_c = false,
+ .want_stack_check = false,
+ .want_valgrind = false,
+ .emit_h = null,
+ .strip = comp.bin_file.options.strip,
+ .is_native_os = false,
+ .self_exe_path = comp.self_exe_path,
+ .verbose_cc = comp.verbose_cc,
+ .verbose_link = comp.bin_file.options.verbose_link,
+ .verbose_tokenize = comp.verbose_tokenize,
+ .verbose_ast = comp.verbose_ast,
+ .verbose_ir = comp.verbose_ir,
+ .verbose_llvm_ir = comp.verbose_llvm_ir,
+ .verbose_cimport = comp.verbose_cimport,
+ .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
+ .clang_passthrough_mode = comp.clang_passthrough_mode,
+ .version = version,
+ .version_script = map_file_path,
+ .override_soname = override_soname,
+ .c_source_files = &c_source_files,
+ .is_compiler_rt_or_libc = true,
+ });
+ defer sub_compilation.destroy();
+
+ try sub_compilation.updateSubCompilation();
+}
diff --git a/src/install_files.h b/src/install_files.h
deleted file mode 100644
index 90dd085aaa..0000000000
--- a/src/install_files.h
+++ /dev/null
@@ -1,1911 +0,0 @@
-#ifndef ZIG_INSTALL_FILES_H
-#define ZIG_INSTALL_FILES_H
-static const char *ZIG_MUSL_SRC_FILES[] = {
-"musl/src/aio/aio.c",
-"musl/src/aio/aio_suspend.c",
-"musl/src/aio/lio_listio.c",
-"musl/src/complex/__cexp.c",
-"musl/src/complex/__cexpf.c",
-"musl/src/complex/cabs.c",
-"musl/src/complex/cabsf.c",
-"musl/src/complex/cabsl.c",
-"musl/src/complex/cacos.c",
-"musl/src/complex/cacosf.c",
-"musl/src/complex/cacosh.c",
-"musl/src/complex/cacoshf.c",
-"musl/src/complex/cacoshl.c",
-"musl/src/complex/cacosl.c",
-"musl/src/complex/carg.c",
-"musl/src/complex/cargf.c",
-"musl/src/complex/cargl.c",
-"musl/src/complex/casin.c",
-"musl/src/complex/casinf.c",
-"musl/src/complex/casinh.c",
-"musl/src/complex/casinhf.c",
-"musl/src/complex/casinhl.c",
-"musl/src/complex/casinl.c",
-"musl/src/complex/catan.c",
-"musl/src/complex/catanf.c",
-"musl/src/complex/catanh.c",
-"musl/src/complex/catanhf.c",
-"musl/src/complex/catanhl.c",
-"musl/src/complex/catanl.c",
-"musl/src/complex/ccos.c",
-"musl/src/complex/ccosf.c",
-"musl/src/complex/ccosh.c",
-"musl/src/complex/ccoshf.c",
-"musl/src/complex/ccoshl.c",
-"musl/src/complex/ccosl.c",
-"musl/src/complex/cexp.c",
-"musl/src/complex/cexpf.c",
-"musl/src/complex/cexpl.c",
-"musl/src/complex/cimag.c",
-"musl/src/complex/cimagf.c",
-"musl/src/complex/cimagl.c",
-"musl/src/complex/clog.c",
-"musl/src/complex/clogf.c",
-"musl/src/complex/clogl.c",
-"musl/src/complex/conj.c",
-"musl/src/complex/conjf.c",
-"musl/src/complex/conjl.c",
-"musl/src/complex/cpow.c",
-"musl/src/complex/cpowf.c",
-"musl/src/complex/cpowl.c",
-"musl/src/complex/cproj.c",
-"musl/src/complex/cprojf.c",
-"musl/src/complex/cprojl.c",
-"musl/src/complex/creal.c",
-"musl/src/complex/crealf.c",
-"musl/src/complex/creall.c",
-"musl/src/complex/csin.c",
-"musl/src/complex/csinf.c",
-"musl/src/complex/csinh.c",
-"musl/src/complex/csinhf.c",
-"musl/src/complex/csinhl.c",
-"musl/src/complex/csinl.c",
-"musl/src/complex/csqrt.c",
-"musl/src/complex/csqrtf.c",
-"musl/src/complex/csqrtl.c",
-"musl/src/complex/ctan.c",
-"musl/src/complex/ctanf.c",
-"musl/src/complex/ctanh.c",
-"musl/src/complex/ctanhf.c",
-"musl/src/complex/ctanhl.c",
-"musl/src/complex/ctanl.c",
-"musl/src/conf/confstr.c",
-"musl/src/conf/fpathconf.c",
-"musl/src/conf/legacy.c",
-"musl/src/conf/pathconf.c",
-"musl/src/conf/sysconf.c",
-"musl/src/crypt/crypt.c",
-"musl/src/crypt/crypt_blowfish.c",
-"musl/src/crypt/crypt_des.c",
-"musl/src/crypt/crypt_md5.c",
-"musl/src/crypt/crypt_r.c",
-"musl/src/crypt/crypt_sha256.c",
-"musl/src/crypt/crypt_sha512.c",
-"musl/src/crypt/encrypt.c",
-"musl/src/ctype/__ctype_b_loc.c",
-"musl/src/ctype/__ctype_get_mb_cur_max.c",
-"musl/src/ctype/__ctype_tolower_loc.c",
-"musl/src/ctype/__ctype_toupper_loc.c",
-"musl/src/ctype/isalnum.c",
-"musl/src/ctype/isalpha.c",
-"musl/src/ctype/isascii.c",
-"musl/src/ctype/isblank.c",
-"musl/src/ctype/iscntrl.c",
-"musl/src/ctype/isdigit.c",
-"musl/src/ctype/isgraph.c",
-"musl/src/ctype/islower.c",
-"musl/src/ctype/isprint.c",
-"musl/src/ctype/ispunct.c",
-"musl/src/ctype/isspace.c",
-"musl/src/ctype/isupper.c",
-"musl/src/ctype/iswalnum.c",
-"musl/src/ctype/iswalpha.c",
-"musl/src/ctype/iswblank.c",
-"musl/src/ctype/iswcntrl.c",
-"musl/src/ctype/iswctype.c",
-"musl/src/ctype/iswdigit.c",
-"musl/src/ctype/iswgraph.c",
-"musl/src/ctype/iswlower.c",
-"musl/src/ctype/iswprint.c",
-"musl/src/ctype/iswpunct.c",
-"musl/src/ctype/iswspace.c",
-"musl/src/ctype/iswupper.c",
-"musl/src/ctype/iswxdigit.c",
-"musl/src/ctype/isxdigit.c",
-"musl/src/ctype/toascii.c",
-"musl/src/ctype/tolower.c",
-"musl/src/ctype/toupper.c",
-"musl/src/ctype/towctrans.c",
-"musl/src/ctype/wcswidth.c",
-"musl/src/ctype/wctrans.c",
-"musl/src/ctype/wcwidth.c",
-"musl/src/dirent/alphasort.c",
-"musl/src/dirent/closedir.c",
-"musl/src/dirent/dirfd.c",
-"musl/src/dirent/fdopendir.c",
-"musl/src/dirent/opendir.c",
-"musl/src/dirent/readdir.c",
-"musl/src/dirent/readdir_r.c",
-"musl/src/dirent/rewinddir.c",
-"musl/src/dirent/scandir.c",
-"musl/src/dirent/seekdir.c",
-"musl/src/dirent/telldir.c",
-"musl/src/dirent/versionsort.c",
-"musl/src/env/__environ.c",
-"musl/src/env/__init_tls.c",
-"musl/src/env/__libc_start_main.c",
-"musl/src/env/__reset_tls.c",
-"musl/src/env/__stack_chk_fail.c",
-"musl/src/env/clearenv.c",
-"musl/src/env/getenv.c",
-"musl/src/env/putenv.c",
-"musl/src/env/secure_getenv.c",
-"musl/src/env/setenv.c",
-"musl/src/env/unsetenv.c",
-"musl/src/errno/__errno_location.c",
-"musl/src/errno/strerror.c",
-"musl/src/exit/_Exit.c",
-"musl/src/exit/abort.c",
-"musl/src/exit/arm/__aeabi_atexit.c",
-"musl/src/exit/assert.c",
-"musl/src/exit/at_quick_exit.c",
-"musl/src/exit/atexit.c",
-"musl/src/exit/exit.c",
-"musl/src/exit/quick_exit.c",
-"musl/src/fcntl/creat.c",
-"musl/src/fcntl/fcntl.c",
-"musl/src/fcntl/open.c",
-"musl/src/fcntl/openat.c",
-"musl/src/fcntl/posix_fadvise.c",
-"musl/src/fcntl/posix_fallocate.c",
-"musl/src/fenv/__flt_rounds.c",
-"musl/src/fenv/aarch64/fenv.s",
-"musl/src/fenv/arm/fenv-hf.S",
-"musl/src/fenv/arm/fenv.c",
-"musl/src/fenv/fegetexceptflag.c",
-"musl/src/fenv/feholdexcept.c",
-"musl/src/fenv/fenv.c",
-"musl/src/fenv/fesetexceptflag.c",
-"musl/src/fenv/fesetround.c",
-"musl/src/fenv/feupdateenv.c",
-"musl/src/fenv/i386/fenv.s",
-"musl/src/fenv/m68k/fenv.c",
-"musl/src/fenv/mips/fenv-sf.c",
-"musl/src/fenv/mips/fenv.S",
-"musl/src/fenv/mips64/fenv-sf.c",
-"musl/src/fenv/mips64/fenv.S",
-"musl/src/fenv/mipsn32/fenv-sf.c",
-"musl/src/fenv/mipsn32/fenv.S",
-"musl/src/fenv/powerpc/fenv-sf.c",
-"musl/src/fenv/powerpc/fenv.S",
-"musl/src/fenv/powerpc64/fenv.c",
-"musl/src/fenv/riscv64/fenv-sf.c",
-"musl/src/fenv/riscv64/fenv.S",
-"musl/src/fenv/s390x/fenv.c",
-"musl/src/fenv/sh/fenv-nofpu.c",
-"musl/src/fenv/sh/fenv.S",
-"musl/src/fenv/x32/fenv.s",
-"musl/src/fenv/x86_64/fenv.s",
-"musl/src/internal/defsysinfo.c",
-"musl/src/internal/floatscan.c",
-"musl/src/internal/i386/defsysinfo.s",
-"musl/src/internal/intscan.c",
-"musl/src/internal/libc.c",
-"musl/src/internal/procfdname.c",
-"musl/src/internal/sh/__shcall.c",
-"musl/src/internal/shgetc.c",
-"musl/src/internal/syscall_ret.c",
-"musl/src/internal/vdso.c",
-"musl/src/internal/version.c",
-"musl/src/ipc/ftok.c",
-"musl/src/ipc/msgctl.c",
-"musl/src/ipc/msgget.c",
-"musl/src/ipc/msgrcv.c",
-"musl/src/ipc/msgsnd.c",
-"musl/src/ipc/semctl.c",
-"musl/src/ipc/semget.c",
-"musl/src/ipc/semop.c",
-"musl/src/ipc/semtimedop.c",
-"musl/src/ipc/shmat.c",
-"musl/src/ipc/shmctl.c",
-"musl/src/ipc/shmdt.c",
-"musl/src/ipc/shmget.c",
-"musl/src/ldso/__dlsym.c",
-"musl/src/ldso/aarch64/dlsym.s",
-"musl/src/ldso/aarch64/tlsdesc.s",
-"musl/src/ldso/arm/dlsym.s",
-"musl/src/ldso/arm/dlsym_time64.S",
-"musl/src/ldso/arm/find_exidx.c",
-"musl/src/ldso/arm/tlsdesc.S",
-"musl/src/ldso/dl_iterate_phdr.c",
-"musl/src/ldso/dladdr.c",
-"musl/src/ldso/dlclose.c",
-"musl/src/ldso/dlerror.c",
-"musl/src/ldso/dlinfo.c",
-"musl/src/ldso/dlopen.c",
-"musl/src/ldso/dlsym.c",
-"musl/src/ldso/i386/dlsym.s",
-"musl/src/ldso/i386/dlsym_time64.S",
-"musl/src/ldso/i386/tlsdesc.s",
-"musl/src/ldso/m68k/dlsym.s",
-"musl/src/ldso/m68k/dlsym_time64.S",
-"musl/src/ldso/microblaze/dlsym.s",
-"musl/src/ldso/microblaze/dlsym_time64.S",
-"musl/src/ldso/mips/dlsym.s",
-"musl/src/ldso/mips/dlsym_time64.S",
-"musl/src/ldso/mips64/dlsym.s",
-"musl/src/ldso/mipsn32/dlsym.s",
-"musl/src/ldso/mipsn32/dlsym_time64.S",
-"musl/src/ldso/or1k/dlsym.s",
-"musl/src/ldso/or1k/dlsym_time64.S",
-"musl/src/ldso/powerpc/dlsym.s",
-"musl/src/ldso/powerpc/dlsym_time64.S",
-"musl/src/ldso/powerpc64/dlsym.s",
-"musl/src/ldso/riscv64/dlsym.s",
-"musl/src/ldso/s390x/dlsym.s",
-"musl/src/ldso/sh/dlsym.s",
-"musl/src/ldso/sh/dlsym_time64.S",
-"musl/src/ldso/tlsdesc.c",
-"musl/src/ldso/x32/dlsym.s",
-"musl/src/ldso/x86_64/dlsym.s",
-"musl/src/ldso/x86_64/tlsdesc.s",
-"musl/src/legacy/cuserid.c",
-"musl/src/legacy/daemon.c",
-"musl/src/legacy/err.c",
-"musl/src/legacy/euidaccess.c",
-"musl/src/legacy/ftw.c",
-"musl/src/legacy/futimes.c",
-"musl/src/legacy/getdtablesize.c",
-"musl/src/legacy/getloadavg.c",
-"musl/src/legacy/getpagesize.c",
-"musl/src/legacy/getpass.c",
-"musl/src/legacy/getusershell.c",
-"musl/src/legacy/isastream.c",
-"musl/src/legacy/lutimes.c",
-"musl/src/legacy/ulimit.c",
-"musl/src/legacy/utmpx.c",
-"musl/src/legacy/valloc.c",
-"musl/src/linux/adjtime.c",
-"musl/src/linux/adjtimex.c",
-"musl/src/linux/arch_prctl.c",
-"musl/src/linux/brk.c",
-"musl/src/linux/cache.c",
-"musl/src/linux/cap.c",
-"musl/src/linux/chroot.c",
-"musl/src/linux/clock_adjtime.c",
-"musl/src/linux/clone.c",
-"musl/src/linux/copy_file_range.c",
-"musl/src/linux/epoll.c",
-"musl/src/linux/eventfd.c",
-"musl/src/linux/fallocate.c",
-"musl/src/linux/fanotify.c",
-"musl/src/linux/flock.c",
-"musl/src/linux/getdents.c",
-"musl/src/linux/getrandom.c",
-"musl/src/linux/inotify.c",
-"musl/src/linux/ioperm.c",
-"musl/src/linux/iopl.c",
-"musl/src/linux/klogctl.c",
-"musl/src/linux/membarrier.c",
-"musl/src/linux/memfd_create.c",
-"musl/src/linux/mlock2.c",
-"musl/src/linux/module.c",
-"musl/src/linux/mount.c",
-"musl/src/linux/name_to_handle_at.c",
-"musl/src/linux/open_by_handle_at.c",
-"musl/src/linux/personality.c",
-"musl/src/linux/pivot_root.c",
-"musl/src/linux/ppoll.c",
-"musl/src/linux/prctl.c",
-"musl/src/linux/prlimit.c",
-"musl/src/linux/process_vm.c",
-"musl/src/linux/ptrace.c",
-"musl/src/linux/quotactl.c",
-"musl/src/linux/readahead.c",
-"musl/src/linux/reboot.c",
-"musl/src/linux/remap_file_pages.c",
-"musl/src/linux/sbrk.c",
-"musl/src/linux/sendfile.c",
-"musl/src/linux/setfsgid.c",
-"musl/src/linux/setfsuid.c",
-"musl/src/linux/setgroups.c",
-"musl/src/linux/sethostname.c",
-"musl/src/linux/setns.c",
-"musl/src/linux/settimeofday.c",
-"musl/src/linux/signalfd.c",
-"musl/src/linux/splice.c",
-"musl/src/linux/stime.c",
-"musl/src/linux/swap.c",
-"musl/src/linux/sync_file_range.c",
-"musl/src/linux/syncfs.c",
-"musl/src/linux/sysinfo.c",
-"musl/src/linux/tee.c",
-"musl/src/linux/timerfd.c",
-"musl/src/linux/unshare.c",
-"musl/src/linux/utimes.c",
-"musl/src/linux/vhangup.c",
-"musl/src/linux/vmsplice.c",
-"musl/src/linux/wait3.c",
-"musl/src/linux/wait4.c",
-"musl/src/linux/x32/sysinfo.c",
-"musl/src/linux/xattr.c",
-"musl/src/locale/__lctrans.c",
-"musl/src/locale/__mo_lookup.c",
-"musl/src/locale/bind_textdomain_codeset.c",
-"musl/src/locale/c_locale.c",
-"musl/src/locale/catclose.c",
-"musl/src/locale/catgets.c",
-"musl/src/locale/catopen.c",
-"musl/src/locale/dcngettext.c",
-"musl/src/locale/duplocale.c",
-"musl/src/locale/freelocale.c",
-"musl/src/locale/iconv.c",
-"musl/src/locale/iconv_close.c",
-"musl/src/locale/langinfo.c",
-"musl/src/locale/locale_map.c",
-"musl/src/locale/localeconv.c",
-"musl/src/locale/newlocale.c",
-"musl/src/locale/pleval.c",
-"musl/src/locale/setlocale.c",
-"musl/src/locale/strcoll.c",
-"musl/src/locale/strfmon.c",
-"musl/src/locale/strxfrm.c",
-"musl/src/locale/textdomain.c",
-"musl/src/locale/uselocale.c",
-"musl/src/locale/wcscoll.c",
-"musl/src/locale/wcsxfrm.c",
-"musl/src/malloc/aligned_alloc.c",
-"musl/src/malloc/expand_heap.c",
-"musl/src/malloc/lite_malloc.c",
-"musl/src/malloc/malloc.c",
-"musl/src/malloc/malloc_usable_size.c",
-"musl/src/malloc/memalign.c",
-"musl/src/malloc/posix_memalign.c",
-"musl/src/math/__cos.c",
-"musl/src/math/__cosdf.c",
-"musl/src/math/__cosl.c",
-"musl/src/math/__expo2.c",
-"musl/src/math/__expo2f.c",
-"musl/src/math/__fpclassify.c",
-"musl/src/math/__fpclassifyf.c",
-"musl/src/math/__fpclassifyl.c",
-"musl/src/math/__invtrigl.c",
-"musl/src/math/__math_divzero.c",
-"musl/src/math/__math_divzerof.c",
-"musl/src/math/__math_invalid.c",
-"musl/src/math/__math_invalidf.c",
-"musl/src/math/__math_oflow.c",
-"musl/src/math/__math_oflowf.c",
-"musl/src/math/__math_uflow.c",
-"musl/src/math/__math_uflowf.c",
-"musl/src/math/__math_xflow.c",
-"musl/src/math/__math_xflowf.c",
-"musl/src/math/__polevll.c",
-"musl/src/math/__rem_pio2.c",
-"musl/src/math/__rem_pio2_large.c",
-"musl/src/math/__rem_pio2f.c",
-"musl/src/math/__rem_pio2l.c",
-"musl/src/math/__signbit.c",
-"musl/src/math/__signbitf.c",
-"musl/src/math/__signbitl.c",
-"musl/src/math/__sin.c",
-"musl/src/math/__sindf.c",
-"musl/src/math/__sinl.c",
-"musl/src/math/__tan.c",
-"musl/src/math/__tandf.c",
-"musl/src/math/__tanl.c",
-"musl/src/math/aarch64/ceil.c",
-"musl/src/math/aarch64/ceilf.c",
-"musl/src/math/aarch64/fabs.c",
-"musl/src/math/aarch64/fabsf.c",
-"musl/src/math/aarch64/floor.c",
-"musl/src/math/aarch64/floorf.c",
-"musl/src/math/aarch64/fma.c",
-"musl/src/math/aarch64/fmaf.c",
-"musl/src/math/aarch64/fmax.c",
-"musl/src/math/aarch64/fmaxf.c",
-"musl/src/math/aarch64/fmin.c",
-"musl/src/math/aarch64/fminf.c",
-"musl/src/math/aarch64/llrint.c",
-"musl/src/math/aarch64/llrintf.c",
-"musl/src/math/aarch64/llround.c",
-"musl/src/math/aarch64/llroundf.c",
-"musl/src/math/aarch64/lrint.c",
-"musl/src/math/aarch64/lrintf.c",
-"musl/src/math/aarch64/lround.c",
-"musl/src/math/aarch64/lroundf.c",
-"musl/src/math/aarch64/nearbyint.c",
-"musl/src/math/aarch64/nearbyintf.c",
-"musl/src/math/aarch64/rint.c",
-"musl/src/math/aarch64/rintf.c",
-"musl/src/math/aarch64/round.c",
-"musl/src/math/aarch64/roundf.c",
-"musl/src/math/aarch64/sqrt.c",
-"musl/src/math/aarch64/sqrtf.c",
-"musl/src/math/aarch64/trunc.c",
-"musl/src/math/aarch64/truncf.c",
-"musl/src/math/acos.c",
-"musl/src/math/acosf.c",
-"musl/src/math/acosh.c",
-"musl/src/math/acoshf.c",
-"musl/src/math/acoshl.c",
-"musl/src/math/acosl.c",
-"musl/src/math/arm/fabs.c",
-"musl/src/math/arm/fabsf.c",
-"musl/src/math/arm/fma.c",
-"musl/src/math/arm/fmaf.c",
-"musl/src/math/arm/sqrt.c",
-"musl/src/math/arm/sqrtf.c",
-"musl/src/math/asin.c",
-"musl/src/math/asinf.c",
-"musl/src/math/asinh.c",
-"musl/src/math/asinhf.c",
-"musl/src/math/asinhl.c",
-"musl/src/math/asinl.c",
-"musl/src/math/atan.c",
-"musl/src/math/atan2.c",
-"musl/src/math/atan2f.c",
-"musl/src/math/atan2l.c",
-"musl/src/math/atanf.c",
-"musl/src/math/atanh.c",
-"musl/src/math/atanhf.c",
-"musl/src/math/atanhl.c",
-"musl/src/math/atanl.c",
-"musl/src/math/cbrt.c",
-"musl/src/math/cbrtf.c",
-"musl/src/math/cbrtl.c",
-"musl/src/math/ceil.c",
-"musl/src/math/ceilf.c",
-"musl/src/math/ceill.c",
-"musl/src/math/copysign.c",
-"musl/src/math/copysignf.c",
-"musl/src/math/copysignl.c",
-"musl/src/math/cos.c",
-"musl/src/math/cosf.c",
-"musl/src/math/cosh.c",
-"musl/src/math/coshf.c",
-"musl/src/math/coshl.c",
-"musl/src/math/cosl.c",
-"musl/src/math/erf.c",
-"musl/src/math/erff.c",
-"musl/src/math/erfl.c",
-"musl/src/math/exp.c",
-"musl/src/math/exp10.c",
-"musl/src/math/exp10f.c",
-"musl/src/math/exp10l.c",
-"musl/src/math/exp2.c",
-"musl/src/math/exp2f.c",
-"musl/src/math/exp2f_data.c",
-"musl/src/math/exp2l.c",
-"musl/src/math/exp_data.c",
-"musl/src/math/expf.c",
-"musl/src/math/expl.c",
-"musl/src/math/expm1.c",
-"musl/src/math/expm1f.c",
-"musl/src/math/expm1l.c",
-"musl/src/math/fabs.c",
-"musl/src/math/fabsf.c",
-"musl/src/math/fabsl.c",
-"musl/src/math/fdim.c",
-"musl/src/math/fdimf.c",
-"musl/src/math/fdiml.c",
-"musl/src/math/finite.c",
-"musl/src/math/finitef.c",
-"musl/src/math/floor.c",
-"musl/src/math/floorf.c",
-"musl/src/math/floorl.c",
-"musl/src/math/fma.c",
-"musl/src/math/fmaf.c",
-"musl/src/math/fmal.c",
-"musl/src/math/fmax.c",
-"musl/src/math/fmaxf.c",
-"musl/src/math/fmaxl.c",
-"musl/src/math/fmin.c",
-"musl/src/math/fminf.c",
-"musl/src/math/fminl.c",
-"musl/src/math/fmod.c",
-"musl/src/math/fmodf.c",
-"musl/src/math/fmodl.c",
-"musl/src/math/frexp.c",
-"musl/src/math/frexpf.c",
-"musl/src/math/frexpl.c",
-"musl/src/math/hypot.c",
-"musl/src/math/hypotf.c",
-"musl/src/math/hypotl.c",
-"musl/src/math/i386/__invtrigl.s",
-"musl/src/math/i386/acos.s",
-"musl/src/math/i386/acosf.s",
-"musl/src/math/i386/acosl.s",
-"musl/src/math/i386/asin.s",
-"musl/src/math/i386/asinf.s",
-"musl/src/math/i386/asinl.s",
-"musl/src/math/i386/atan.s",
-"musl/src/math/i386/atan2.s",
-"musl/src/math/i386/atan2f.s",
-"musl/src/math/i386/atan2l.s",
-"musl/src/math/i386/atanf.s",
-"musl/src/math/i386/atanl.s",
-"musl/src/math/i386/ceil.s",
-"musl/src/math/i386/ceilf.s",
-"musl/src/math/i386/ceill.s",
-"musl/src/math/i386/exp2l.s",
-"musl/src/math/i386/exp_ld.s",
-"musl/src/math/i386/expl.s",
-"musl/src/math/i386/expm1l.s",
-"musl/src/math/i386/fabs.s",
-"musl/src/math/i386/fabsf.s",
-"musl/src/math/i386/fabsl.s",
-"musl/src/math/i386/floor.s",
-"musl/src/math/i386/floorf.s",
-"musl/src/math/i386/floorl.s",
-"musl/src/math/i386/fmod.s",
-"musl/src/math/i386/fmodf.s",
-"musl/src/math/i386/fmodl.s",
-"musl/src/math/i386/hypot.s",
-"musl/src/math/i386/hypotf.s",
-"musl/src/math/i386/ldexp.s",
-"musl/src/math/i386/ldexpf.s",
-"musl/src/math/i386/ldexpl.s",
-"musl/src/math/i386/llrint.s",
-"musl/src/math/i386/llrintf.s",
-"musl/src/math/i386/llrintl.s",
-"musl/src/math/i386/log.s",
-"musl/src/math/i386/log10.s",
-"musl/src/math/i386/log10f.s",
-"musl/src/math/i386/log10l.s",
-"musl/src/math/i386/log1p.s",
-"musl/src/math/i386/log1pf.s",
-"musl/src/math/i386/log1pl.s",
-"musl/src/math/i386/log2.s",
-"musl/src/math/i386/log2f.s",
-"musl/src/math/i386/log2l.s",
-"musl/src/math/i386/logf.s",
-"musl/src/math/i386/logl.s",
-"musl/src/math/i386/lrint.s",
-"musl/src/math/i386/lrintf.s",
-"musl/src/math/i386/lrintl.s",
-"musl/src/math/i386/remainder.s",
-"musl/src/math/i386/remainderf.s",
-"musl/src/math/i386/remainderl.s",
-"musl/src/math/i386/remquo.s",
-"musl/src/math/i386/remquof.s",
-"musl/src/math/i386/remquol.s",
-"musl/src/math/i386/rint.s",
-"musl/src/math/i386/rintf.s",
-"musl/src/math/i386/rintl.s",
-"musl/src/math/i386/scalbln.s",
-"musl/src/math/i386/scalblnf.s",
-"musl/src/math/i386/scalblnl.s",
-"musl/src/math/i386/scalbn.s",
-"musl/src/math/i386/scalbnf.s",
-"musl/src/math/i386/scalbnl.s",
-"musl/src/math/i386/sqrt.s",
-"musl/src/math/i386/sqrtf.s",
-"musl/src/math/i386/sqrtl.s",
-"musl/src/math/i386/trunc.s",
-"musl/src/math/i386/truncf.s",
-"musl/src/math/i386/truncl.s",
-"musl/src/math/ilogb.c",
-"musl/src/math/ilogbf.c",
-"musl/src/math/ilogbl.c",
-"musl/src/math/j0.c",
-"musl/src/math/j0f.c",
-"musl/src/math/j1.c",
-"musl/src/math/j1f.c",
-"musl/src/math/jn.c",
-"musl/src/math/jnf.c",
-"musl/src/math/ldexp.c",
-"musl/src/math/ldexpf.c",
-"musl/src/math/ldexpl.c",
-"musl/src/math/lgamma.c",
-"musl/src/math/lgamma_r.c",
-"musl/src/math/lgammaf.c",
-"musl/src/math/lgammaf_r.c",
-"musl/src/math/lgammal.c",
-"musl/src/math/llrint.c",
-"musl/src/math/llrintf.c",
-"musl/src/math/llrintl.c",
-"musl/src/math/llround.c",
-"musl/src/math/llroundf.c",
-"musl/src/math/llroundl.c",
-"musl/src/math/log.c",
-"musl/src/math/log10.c",
-"musl/src/math/log10f.c",
-"musl/src/math/log10l.c",
-"musl/src/math/log1p.c",
-"musl/src/math/log1pf.c",
-"musl/src/math/log1pl.c",
-"musl/src/math/log2.c",
-"musl/src/math/log2_data.c",
-"musl/src/math/log2f.c",
-"musl/src/math/log2f_data.c",
-"musl/src/math/log2l.c",
-"musl/src/math/log_data.c",
-"musl/src/math/logb.c",
-"musl/src/math/logbf.c",
-"musl/src/math/logbl.c",
-"musl/src/math/logf.c",
-"musl/src/math/logf_data.c",
-"musl/src/math/logl.c",
-"musl/src/math/lrint.c",
-"musl/src/math/lrintf.c",
-"musl/src/math/lrintl.c",
-"musl/src/math/lround.c",
-"musl/src/math/lroundf.c",
-"musl/src/math/lroundl.c",
-"musl/src/math/mips/fabs.c",
-"musl/src/math/mips/fabsf.c",
-"musl/src/math/mips/sqrt.c",
-"musl/src/math/mips/sqrtf.c",
-"musl/src/math/modf.c",
-"musl/src/math/modff.c",
-"musl/src/math/modfl.c",
-"musl/src/math/nan.c",
-"musl/src/math/nanf.c",
-"musl/src/math/nanl.c",
-"musl/src/math/nearbyint.c",
-"musl/src/math/nearbyintf.c",
-"musl/src/math/nearbyintl.c",
-"musl/src/math/nextafter.c",
-"musl/src/math/nextafterf.c",
-"musl/src/math/nextafterl.c",
-"musl/src/math/nexttoward.c",
-"musl/src/math/nexttowardf.c",
-"musl/src/math/nexttowardl.c",
-"musl/src/math/pow.c",
-"musl/src/math/pow_data.c",
-"musl/src/math/powerpc/fabs.c",
-"musl/src/math/powerpc/fabsf.c",
-"musl/src/math/powerpc/fma.c",
-"musl/src/math/powerpc/fmaf.c",
-"musl/src/math/powerpc/sqrt.c",
-"musl/src/math/powerpc/sqrtf.c",
-"musl/src/math/powerpc64/ceil.c",
-"musl/src/math/powerpc64/ceilf.c",
-"musl/src/math/powerpc64/fabs.c",
-"musl/src/math/powerpc64/fabsf.c",
-"musl/src/math/powerpc64/floor.c",
-"musl/src/math/powerpc64/floorf.c",
-"musl/src/math/powerpc64/fma.c",
-"musl/src/math/powerpc64/fmaf.c",
-"musl/src/math/powerpc64/fmax.c",
-"musl/src/math/powerpc64/fmaxf.c",
-"musl/src/math/powerpc64/fmin.c",
-"musl/src/math/powerpc64/fminf.c",
-"musl/src/math/powerpc64/lrint.c",
-"musl/src/math/powerpc64/lrintf.c",
-"musl/src/math/powerpc64/lround.c",
-"musl/src/math/powerpc64/lroundf.c",
-"musl/src/math/powerpc64/round.c",
-"musl/src/math/powerpc64/roundf.c",
-"musl/src/math/powerpc64/sqrt.c",
-"musl/src/math/powerpc64/sqrtf.c",
-"musl/src/math/powerpc64/trunc.c",
-"musl/src/math/powerpc64/truncf.c",
-"musl/src/math/powf.c",
-"musl/src/math/powf_data.c",
-"musl/src/math/powl.c",
-"musl/src/math/remainder.c",
-"musl/src/math/remainderf.c",
-"musl/src/math/remainderl.c",
-"musl/src/math/remquo.c",
-"musl/src/math/remquof.c",
-"musl/src/math/remquol.c",
-"musl/src/math/rint.c",
-"musl/src/math/rintf.c",
-"musl/src/math/rintl.c",
-"musl/src/math/riscv64/copysign.c",
-"musl/src/math/riscv64/copysignf.c",
-"musl/src/math/riscv64/fabs.c",
-"musl/src/math/riscv64/fabsf.c",
-"musl/src/math/riscv64/fma.c",
-"musl/src/math/riscv64/fmaf.c",
-"musl/src/math/riscv64/fmax.c",
-"musl/src/math/riscv64/fmaxf.c",
-"musl/src/math/riscv64/fmin.c",
-"musl/src/math/riscv64/fminf.c",
-"musl/src/math/riscv64/sqrt.c",
-"musl/src/math/riscv64/sqrtf.c",
-"musl/src/math/round.c",
-"musl/src/math/roundf.c",
-"musl/src/math/roundl.c",
-"musl/src/math/s390x/ceil.c",
-"musl/src/math/s390x/ceilf.c",
-"musl/src/math/s390x/ceill.c",
-"musl/src/math/s390x/fabs.c",
-"musl/src/math/s390x/fabsf.c",
-"musl/src/math/s390x/fabsl.c",
-"musl/src/math/s390x/floor.c",
-"musl/src/math/s390x/floorf.c",
-"musl/src/math/s390x/floorl.c",
-"musl/src/math/s390x/fma.c",
-"musl/src/math/s390x/fmaf.c",
-"musl/src/math/s390x/nearbyint.c",
-"musl/src/math/s390x/nearbyintf.c",
-"musl/src/math/s390x/nearbyintl.c",
-"musl/src/math/s390x/rint.c",
-"musl/src/math/s390x/rintf.c",
-"musl/src/math/s390x/rintl.c",
-"musl/src/math/s390x/round.c",
-"musl/src/math/s390x/roundf.c",
-"musl/src/math/s390x/roundl.c",
-"musl/src/math/s390x/sqrt.c",
-"musl/src/math/s390x/sqrtf.c",
-"musl/src/math/s390x/sqrtl.c",
-"musl/src/math/s390x/trunc.c",
-"musl/src/math/s390x/truncf.c",
-"musl/src/math/s390x/truncl.c",
-"musl/src/math/scalb.c",
-"musl/src/math/scalbf.c",
-"musl/src/math/scalbln.c",
-"musl/src/math/scalblnf.c",
-"musl/src/math/scalblnl.c",
-"musl/src/math/scalbn.c",
-"musl/src/math/scalbnf.c",
-"musl/src/math/scalbnl.c",
-"musl/src/math/signgam.c",
-"musl/src/math/significand.c",
-"musl/src/math/significandf.c",
-"musl/src/math/sin.c",
-"musl/src/math/sincos.c",
-"musl/src/math/sincosf.c",
-"musl/src/math/sincosl.c",
-"musl/src/math/sinf.c",
-"musl/src/math/sinh.c",
-"musl/src/math/sinhf.c",
-"musl/src/math/sinhl.c",
-"musl/src/math/sinl.c",
-"musl/src/math/sqrt.c",
-"musl/src/math/sqrtf.c",
-"musl/src/math/sqrtl.c",
-"musl/src/math/tan.c",
-"musl/src/math/tanf.c",
-"musl/src/math/tanh.c",
-"musl/src/math/tanhf.c",
-"musl/src/math/tanhl.c",
-"musl/src/math/tanl.c",
-"musl/src/math/tgamma.c",
-"musl/src/math/tgammaf.c",
-"musl/src/math/tgammal.c",
-"musl/src/math/trunc.c",
-"musl/src/math/truncf.c",
-"musl/src/math/truncl.c",
-"musl/src/math/x32/__invtrigl.s",
-"musl/src/math/x32/acosl.s",
-"musl/src/math/x32/asinl.s",
-"musl/src/math/x32/atan2l.s",
-"musl/src/math/x32/atanl.s",
-"musl/src/math/x32/ceill.s",
-"musl/src/math/x32/exp2l.s",
-"musl/src/math/x32/expl.s",
-"musl/src/math/x32/expm1l.s",
-"musl/src/math/x32/fabs.s",
-"musl/src/math/x32/fabsf.s",
-"musl/src/math/x32/fabsl.s",
-"musl/src/math/x32/floorl.s",
-"musl/src/math/x32/fma.c",
-"musl/src/math/x32/fmaf.c",
-"musl/src/math/x32/fmodl.s",
-"musl/src/math/x32/llrint.s",
-"musl/src/math/x32/llrintf.s",
-"musl/src/math/x32/llrintl.s",
-"musl/src/math/x32/log10l.s",
-"musl/src/math/x32/log1pl.s",
-"musl/src/math/x32/log2l.s",
-"musl/src/math/x32/logl.s",
-"musl/src/math/x32/lrint.s",
-"musl/src/math/x32/lrintf.s",
-"musl/src/math/x32/lrintl.s",
-"musl/src/math/x32/remainderl.s",
-"musl/src/math/x32/rintl.s",
-"musl/src/math/x32/sqrt.s",
-"musl/src/math/x32/sqrtf.s",
-"musl/src/math/x32/sqrtl.s",
-"musl/src/math/x32/truncl.s",
-"musl/src/math/x86_64/__invtrigl.s",
-"musl/src/math/x86_64/acosl.s",
-"musl/src/math/x86_64/asinl.s",
-"musl/src/math/x86_64/atan2l.s",
-"musl/src/math/x86_64/atanl.s",
-"musl/src/math/x86_64/ceill.s",
-"musl/src/math/x86_64/exp2l.s",
-"musl/src/math/x86_64/expl.s",
-"musl/src/math/x86_64/expm1l.s",
-"musl/src/math/x86_64/fabs.s",
-"musl/src/math/x86_64/fabsf.s",
-"musl/src/math/x86_64/fabsl.s",
-"musl/src/math/x86_64/floorl.s",
-"musl/src/math/x86_64/fma.c",
-"musl/src/math/x86_64/fmaf.c",
-"musl/src/math/x86_64/fmodl.s",
-"musl/src/math/x86_64/llrint.s",
-"musl/src/math/x86_64/llrintf.s",
-"musl/src/math/x86_64/llrintl.s",
-"musl/src/math/x86_64/log10l.s",
-"musl/src/math/x86_64/log1pl.s",
-"musl/src/math/x86_64/log2l.s",
-"musl/src/math/x86_64/logl.s",
-"musl/src/math/x86_64/lrint.s",
-"musl/src/math/x86_64/lrintf.s",
-"musl/src/math/x86_64/lrintl.s",
-"musl/src/math/x86_64/remainderl.s",
-"musl/src/math/x86_64/rintl.s",
-"musl/src/math/x86_64/sqrt.s",
-"musl/src/math/x86_64/sqrtf.s",
-"musl/src/math/x86_64/sqrtl.s",
-"musl/src/math/x86_64/truncl.s",
-"musl/src/misc/a64l.c",
-"musl/src/misc/basename.c",
-"musl/src/misc/dirname.c",
-"musl/src/misc/ffs.c",
-"musl/src/misc/ffsl.c",
-"musl/src/misc/ffsll.c",
-"musl/src/misc/fmtmsg.c",
-"musl/src/misc/forkpty.c",
-"musl/src/misc/get_current_dir_name.c",
-"musl/src/misc/getauxval.c",
-"musl/src/misc/getdomainname.c",
-"musl/src/misc/getentropy.c",
-"musl/src/misc/gethostid.c",
-"musl/src/misc/getopt.c",
-"musl/src/misc/getopt_long.c",
-"musl/src/misc/getpriority.c",
-"musl/src/misc/getresgid.c",
-"musl/src/misc/getresuid.c",
-"musl/src/misc/getrlimit.c",
-"musl/src/misc/getrusage.c",
-"musl/src/misc/getsubopt.c",
-"musl/src/misc/initgroups.c",
-"musl/src/misc/ioctl.c",
-"musl/src/misc/issetugid.c",
-"musl/src/misc/lockf.c",
-"musl/src/misc/login_tty.c",
-"musl/src/misc/mntent.c",
-"musl/src/misc/nftw.c",
-"musl/src/misc/openpty.c",
-"musl/src/misc/ptsname.c",
-"musl/src/misc/pty.c",
-"musl/src/misc/realpath.c",
-"musl/src/misc/setdomainname.c",
-"musl/src/misc/setpriority.c",
-"musl/src/misc/setrlimit.c",
-"musl/src/misc/syscall.c",
-"musl/src/misc/syslog.c",
-"musl/src/misc/uname.c",
-"musl/src/misc/wordexp.c",
-"musl/src/mman/madvise.c",
-"musl/src/mman/mincore.c",
-"musl/src/mman/mlock.c",
-"musl/src/mman/mlockall.c",
-"musl/src/mman/mmap.c",
-"musl/src/mman/mprotect.c",
-"musl/src/mman/mremap.c",
-"musl/src/mman/msync.c",
-"musl/src/mman/munlock.c",
-"musl/src/mman/munlockall.c",
-"musl/src/mman/munmap.c",
-"musl/src/mman/posix_madvise.c",
-"musl/src/mman/shm_open.c",
-"musl/src/mq/mq_close.c",
-"musl/src/mq/mq_getattr.c",
-"musl/src/mq/mq_notify.c",
-"musl/src/mq/mq_open.c",
-"musl/src/mq/mq_receive.c",
-"musl/src/mq/mq_send.c",
-"musl/src/mq/mq_setattr.c",
-"musl/src/mq/mq_timedreceive.c",
-"musl/src/mq/mq_timedsend.c",
-"musl/src/mq/mq_unlink.c",
-"musl/src/multibyte/btowc.c",
-"musl/src/multibyte/c16rtomb.c",
-"musl/src/multibyte/c32rtomb.c",
-"musl/src/multibyte/internal.c",
-"musl/src/multibyte/mblen.c",
-"musl/src/multibyte/mbrlen.c",
-"musl/src/multibyte/mbrtoc16.c",
-"musl/src/multibyte/mbrtoc32.c",
-"musl/src/multibyte/mbrtowc.c",
-"musl/src/multibyte/mbsinit.c",
-"musl/src/multibyte/mbsnrtowcs.c",
-"musl/src/multibyte/mbsrtowcs.c",
-"musl/src/multibyte/mbstowcs.c",
-"musl/src/multibyte/mbtowc.c",
-"musl/src/multibyte/wcrtomb.c",
-"musl/src/multibyte/wcsnrtombs.c",
-"musl/src/multibyte/wcsrtombs.c",
-"musl/src/multibyte/wcstombs.c",
-"musl/src/multibyte/wctob.c",
-"musl/src/multibyte/wctomb.c",
-"musl/src/network/accept.c",
-"musl/src/network/accept4.c",
-"musl/src/network/bind.c",
-"musl/src/network/connect.c",
-"musl/src/network/dn_comp.c",
-"musl/src/network/dn_expand.c",
-"musl/src/network/dn_skipname.c",
-"musl/src/network/dns_parse.c",
-"musl/src/network/ent.c",
-"musl/src/network/ether.c",
-"musl/src/network/freeaddrinfo.c",
-"musl/src/network/gai_strerror.c",
-"musl/src/network/getaddrinfo.c",
-"musl/src/network/gethostbyaddr.c",
-"musl/src/network/gethostbyaddr_r.c",
-"musl/src/network/gethostbyname.c",
-"musl/src/network/gethostbyname2.c",
-"musl/src/network/gethostbyname2_r.c",
-"musl/src/network/gethostbyname_r.c",
-"musl/src/network/getifaddrs.c",
-"musl/src/network/getnameinfo.c",
-"musl/src/network/getpeername.c",
-"musl/src/network/getservbyname.c",
-"musl/src/network/getservbyname_r.c",
-"musl/src/network/getservbyport.c",
-"musl/src/network/getservbyport_r.c",
-"musl/src/network/getsockname.c",
-"musl/src/network/getsockopt.c",
-"musl/src/network/h_errno.c",
-"musl/src/network/herror.c",
-"musl/src/network/hstrerror.c",
-"musl/src/network/htonl.c",
-"musl/src/network/htons.c",
-"musl/src/network/if_freenameindex.c",
-"musl/src/network/if_indextoname.c",
-"musl/src/network/if_nameindex.c",
-"musl/src/network/if_nametoindex.c",
-"musl/src/network/in6addr_any.c",
-"musl/src/network/in6addr_loopback.c",
-"musl/src/network/inet_addr.c",
-"musl/src/network/inet_aton.c",
-"musl/src/network/inet_legacy.c",
-"musl/src/network/inet_ntoa.c",
-"musl/src/network/inet_ntop.c",
-"musl/src/network/inet_pton.c",
-"musl/src/network/listen.c",
-"musl/src/network/lookup_ipliteral.c",
-"musl/src/network/lookup_name.c",
-"musl/src/network/lookup_serv.c",
-"musl/src/network/netlink.c",
-"musl/src/network/netname.c",
-"musl/src/network/ns_parse.c",
-"musl/src/network/ntohl.c",
-"musl/src/network/ntohs.c",
-"musl/src/network/proto.c",
-"musl/src/network/recv.c",
-"musl/src/network/recvfrom.c",
-"musl/src/network/recvmmsg.c",
-"musl/src/network/recvmsg.c",
-"musl/src/network/res_init.c",
-"musl/src/network/res_mkquery.c",
-"musl/src/network/res_msend.c",
-"musl/src/network/res_query.c",
-"musl/src/network/res_querydomain.c",
-"musl/src/network/res_send.c",
-"musl/src/network/res_state.c",
-"musl/src/network/resolvconf.c",
-"musl/src/network/send.c",
-"musl/src/network/sendmmsg.c",
-"musl/src/network/sendmsg.c",
-"musl/src/network/sendto.c",
-"musl/src/network/serv.c",
-"musl/src/network/setsockopt.c",
-"musl/src/network/shutdown.c",
-"musl/src/network/sockatmark.c",
-"musl/src/network/socket.c",
-"musl/src/network/socketpair.c",
-"musl/src/passwd/fgetgrent.c",
-"musl/src/passwd/fgetpwent.c",
-"musl/src/passwd/fgetspent.c",
-"musl/src/passwd/getgr_a.c",
-"musl/src/passwd/getgr_r.c",
-"musl/src/passwd/getgrent.c",
-"musl/src/passwd/getgrent_a.c",
-"musl/src/passwd/getgrouplist.c",
-"musl/src/passwd/getpw_a.c",
-"musl/src/passwd/getpw_r.c",
-"musl/src/passwd/getpwent.c",
-"musl/src/passwd/getpwent_a.c",
-"musl/src/passwd/getspent.c",
-"musl/src/passwd/getspnam.c",
-"musl/src/passwd/getspnam_r.c",
-"musl/src/passwd/lckpwdf.c",
-"musl/src/passwd/nscd_query.c",
-"musl/src/passwd/putgrent.c",
-"musl/src/passwd/putpwent.c",
-"musl/src/passwd/putspent.c",
-"musl/src/prng/__rand48_step.c",
-"musl/src/prng/__seed48.c",
-"musl/src/prng/drand48.c",
-"musl/src/prng/lcong48.c",
-"musl/src/prng/lrand48.c",
-"musl/src/prng/mrand48.c",
-"musl/src/prng/rand.c",
-"musl/src/prng/rand_r.c",
-"musl/src/prng/random.c",
-"musl/src/prng/seed48.c",
-"musl/src/prng/srand48.c",
-"musl/src/process/arm/vfork.s",
-"musl/src/process/execl.c",
-"musl/src/process/execle.c",
-"musl/src/process/execlp.c",
-"musl/src/process/execv.c",
-"musl/src/process/execve.c",
-"musl/src/process/execvp.c",
-"musl/src/process/fexecve.c",
-"musl/src/process/fork.c",
-"musl/src/process/i386/vfork.s",
-"musl/src/process/posix_spawn.c",
-"musl/src/process/posix_spawn_file_actions_addchdir.c",
-"musl/src/process/posix_spawn_file_actions_addclose.c",
-"musl/src/process/posix_spawn_file_actions_adddup2.c",
-"musl/src/process/posix_spawn_file_actions_addfchdir.c",
-"musl/src/process/posix_spawn_file_actions_addopen.c",
-"musl/src/process/posix_spawn_file_actions_destroy.c",
-"musl/src/process/posix_spawn_file_actions_init.c",
-"musl/src/process/posix_spawnattr_destroy.c",
-"musl/src/process/posix_spawnattr_getflags.c",
-"musl/src/process/posix_spawnattr_getpgroup.c",
-"musl/src/process/posix_spawnattr_getsigdefault.c",
-"musl/src/process/posix_spawnattr_getsigmask.c",
-"musl/src/process/posix_spawnattr_init.c",
-"musl/src/process/posix_spawnattr_sched.c",
-"musl/src/process/posix_spawnattr_setflags.c",
-"musl/src/process/posix_spawnattr_setpgroup.c",
-"musl/src/process/posix_spawnattr_setsigdefault.c",
-"musl/src/process/posix_spawnattr_setsigmask.c",
-"musl/src/process/posix_spawnp.c",
-"musl/src/process/s390x/vfork.s",
-"musl/src/process/sh/vfork.s",
-"musl/src/process/system.c",
-"musl/src/process/vfork.c",
-"musl/src/process/wait.c",
-"musl/src/process/waitid.c",
-"musl/src/process/waitpid.c",
-"musl/src/process/x32/vfork.s",
-"musl/src/process/x86_64/vfork.s",
-"musl/src/regex/fnmatch.c",
-"musl/src/regex/glob.c",
-"musl/src/regex/regcomp.c",
-"musl/src/regex/regerror.c",
-"musl/src/regex/regexec.c",
-"musl/src/regex/tre-mem.c",
-"musl/src/sched/affinity.c",
-"musl/src/sched/sched_cpucount.c",
-"musl/src/sched/sched_get_priority_max.c",
-"musl/src/sched/sched_getcpu.c",
-"musl/src/sched/sched_getparam.c",
-"musl/src/sched/sched_getscheduler.c",
-"musl/src/sched/sched_rr_get_interval.c",
-"musl/src/sched/sched_setparam.c",
-"musl/src/sched/sched_setscheduler.c",
-"musl/src/sched/sched_yield.c",
-"musl/src/search/hsearch.c",
-"musl/src/search/insque.c",
-"musl/src/search/lsearch.c",
-"musl/src/search/tdelete.c",
-"musl/src/search/tdestroy.c",
-"musl/src/search/tfind.c",
-"musl/src/search/tsearch.c",
-"musl/src/search/twalk.c",
-"musl/src/select/poll.c",
-"musl/src/select/pselect.c",
-"musl/src/select/select.c",
-"musl/src/setjmp/aarch64/longjmp.s",
-"musl/src/setjmp/aarch64/setjmp.s",
-"musl/src/setjmp/arm/longjmp.S",
-"musl/src/setjmp/arm/setjmp.S",
-"musl/src/setjmp/i386/longjmp.s",
-"musl/src/setjmp/i386/setjmp.s",
-"musl/src/setjmp/longjmp.c",
-"musl/src/setjmp/m68k/longjmp.s",
-"musl/src/setjmp/m68k/setjmp.s",
-"musl/src/setjmp/microblaze/longjmp.s",
-"musl/src/setjmp/microblaze/setjmp.s",
-"musl/src/setjmp/mips/longjmp.S",
-"musl/src/setjmp/mips/setjmp.S",
-"musl/src/setjmp/mips64/longjmp.S",
-"musl/src/setjmp/mips64/setjmp.S",
-"musl/src/setjmp/mipsn32/longjmp.S",
-"musl/src/setjmp/mipsn32/setjmp.S",
-"musl/src/setjmp/or1k/longjmp.s",
-"musl/src/setjmp/or1k/setjmp.s",
-"musl/src/setjmp/powerpc/longjmp.S",
-"musl/src/setjmp/powerpc/setjmp.S",
-"musl/src/setjmp/powerpc64/longjmp.s",
-"musl/src/setjmp/powerpc64/setjmp.s",
-"musl/src/setjmp/riscv64/longjmp.S",
-"musl/src/setjmp/riscv64/setjmp.S",
-"musl/src/setjmp/s390x/longjmp.s",
-"musl/src/setjmp/s390x/setjmp.s",
-"musl/src/setjmp/setjmp.c",
-"musl/src/setjmp/sh/longjmp.S",
-"musl/src/setjmp/sh/setjmp.S",
-"musl/src/setjmp/x32/longjmp.s",
-"musl/src/setjmp/x32/setjmp.s",
-"musl/src/setjmp/x86_64/longjmp.s",
-"musl/src/setjmp/x86_64/setjmp.s",
-"musl/src/signal/aarch64/restore.s",
-"musl/src/signal/aarch64/sigsetjmp.s",
-"musl/src/signal/arm/restore.s",
-"musl/src/signal/arm/sigsetjmp.s",
-"musl/src/signal/block.c",
-"musl/src/signal/getitimer.c",
-"musl/src/signal/i386/restore.s",
-"musl/src/signal/i386/sigsetjmp.s",
-"musl/src/signal/kill.c",
-"musl/src/signal/killpg.c",
-"musl/src/signal/m68k/sigsetjmp.s",
-"musl/src/signal/microblaze/restore.s",
-"musl/src/signal/microblaze/sigsetjmp.s",
-"musl/src/signal/mips/restore.s",
-"musl/src/signal/mips/sigsetjmp.s",
-"musl/src/signal/mips64/restore.s",
-"musl/src/signal/mips64/sigsetjmp.s",
-"musl/src/signal/mipsn32/restore.s",
-"musl/src/signal/mipsn32/sigsetjmp.s",
-"musl/src/signal/or1k/sigsetjmp.s",
-"musl/src/signal/powerpc/restore.s",
-"musl/src/signal/powerpc/sigsetjmp.s",
-"musl/src/signal/powerpc64/restore.s",
-"musl/src/signal/powerpc64/sigsetjmp.s",
-"musl/src/signal/psiginfo.c",
-"musl/src/signal/psignal.c",
-"musl/src/signal/raise.c",
-"musl/src/signal/restore.c",
-"musl/src/signal/riscv64/restore.s",
-"musl/src/signal/riscv64/sigsetjmp.s",
-"musl/src/signal/s390x/restore.s",
-"musl/src/signal/s390x/sigsetjmp.s",
-"musl/src/signal/setitimer.c",
-"musl/src/signal/sh/restore.s",
-"musl/src/signal/sh/sigsetjmp.s",
-"musl/src/signal/sigaction.c",
-"musl/src/signal/sigaddset.c",
-"musl/src/signal/sigaltstack.c",
-"musl/src/signal/sigandset.c",
-"musl/src/signal/sigdelset.c",
-"musl/src/signal/sigemptyset.c",
-"musl/src/signal/sigfillset.c",
-"musl/src/signal/sighold.c",
-"musl/src/signal/sigignore.c",
-"musl/src/signal/siginterrupt.c",
-"musl/src/signal/sigisemptyset.c",
-"musl/src/signal/sigismember.c",
-"musl/src/signal/siglongjmp.c",
-"musl/src/signal/signal.c",
-"musl/src/signal/sigorset.c",
-"musl/src/signal/sigpause.c",
-"musl/src/signal/sigpending.c",
-"musl/src/signal/sigprocmask.c",
-"musl/src/signal/sigqueue.c",
-"musl/src/signal/sigrelse.c",
-"musl/src/signal/sigrtmax.c",
-"musl/src/signal/sigrtmin.c",
-"musl/src/signal/sigset.c",
-"musl/src/signal/sigsetjmp.c",
-"musl/src/signal/sigsetjmp_tail.c",
-"musl/src/signal/sigsuspend.c",
-"musl/src/signal/sigtimedwait.c",
-"musl/src/signal/sigwait.c",
-"musl/src/signal/sigwaitinfo.c",
-"musl/src/signal/x32/getitimer.c",
-"musl/src/signal/x32/restore.s",
-"musl/src/signal/x32/setitimer.c",
-"musl/src/signal/x32/sigsetjmp.s",
-"musl/src/signal/x86_64/restore.s",
-"musl/src/signal/x86_64/sigsetjmp.s",
-"musl/src/stat/__xstat.c",
-"musl/src/stat/chmod.c",
-"musl/src/stat/fchmod.c",
-"musl/src/stat/fchmodat.c",
-"musl/src/stat/fstat.c",
-"musl/src/stat/fstatat.c",
-"musl/src/stat/futimens.c",
-"musl/src/stat/futimesat.c",
-"musl/src/stat/lchmod.c",
-"musl/src/stat/lstat.c",
-"musl/src/stat/mkdir.c",
-"musl/src/stat/mkdirat.c",
-"musl/src/stat/mkfifo.c",
-"musl/src/stat/mkfifoat.c",
-"musl/src/stat/mknod.c",
-"musl/src/stat/mknodat.c",
-"musl/src/stat/stat.c",
-"musl/src/stat/statvfs.c",
-"musl/src/stat/umask.c",
-"musl/src/stat/utimensat.c",
-"musl/src/stdio/__fclose_ca.c",
-"musl/src/stdio/__fdopen.c",
-"musl/src/stdio/__fmodeflags.c",
-"musl/src/stdio/__fopen_rb_ca.c",
-"musl/src/stdio/__lockfile.c",
-"musl/src/stdio/__overflow.c",
-"musl/src/stdio/__stdio_close.c",
-"musl/src/stdio/__stdio_exit.c",
-"musl/src/stdio/__stdio_read.c",
-"musl/src/stdio/__stdio_seek.c",
-"musl/src/stdio/__stdio_write.c",
-"musl/src/stdio/__stdout_write.c",
-"musl/src/stdio/__string_read.c",
-"musl/src/stdio/__toread.c",
-"musl/src/stdio/__towrite.c",
-"musl/src/stdio/__uflow.c",
-"musl/src/stdio/asprintf.c",
-"musl/src/stdio/clearerr.c",
-"musl/src/stdio/dprintf.c",
-"musl/src/stdio/ext.c",
-"musl/src/stdio/ext2.c",
-"musl/src/stdio/fclose.c",
-"musl/src/stdio/feof.c",
-"musl/src/stdio/ferror.c",
-"musl/src/stdio/fflush.c",
-"musl/src/stdio/fgetc.c",
-"musl/src/stdio/fgetln.c",
-"musl/src/stdio/fgetpos.c",
-"musl/src/stdio/fgets.c",
-"musl/src/stdio/fgetwc.c",
-"musl/src/stdio/fgetws.c",
-"musl/src/stdio/fileno.c",
-"musl/src/stdio/flockfile.c",
-"musl/src/stdio/fmemopen.c",
-"musl/src/stdio/fopen.c",
-"musl/src/stdio/fopencookie.c",
-"musl/src/stdio/fprintf.c",
-"musl/src/stdio/fputc.c",
-"musl/src/stdio/fputs.c",
-"musl/src/stdio/fputwc.c",
-"musl/src/stdio/fputws.c",
-"musl/src/stdio/fread.c",
-"musl/src/stdio/freopen.c",
-"musl/src/stdio/fscanf.c",
-"musl/src/stdio/fseek.c",
-"musl/src/stdio/fsetpos.c",
-"musl/src/stdio/ftell.c",
-"musl/src/stdio/ftrylockfile.c",
-"musl/src/stdio/funlockfile.c",
-"musl/src/stdio/fwide.c",
-"musl/src/stdio/fwprintf.c",
-"musl/src/stdio/fwrite.c",
-"musl/src/stdio/fwscanf.c",
-"musl/src/stdio/getc.c",
-"musl/src/stdio/getc_unlocked.c",
-"musl/src/stdio/getchar.c",
-"musl/src/stdio/getchar_unlocked.c",
-"musl/src/stdio/getdelim.c",
-"musl/src/stdio/getline.c",
-"musl/src/stdio/gets.c",
-"musl/src/stdio/getw.c",
-"musl/src/stdio/getwc.c",
-"musl/src/stdio/getwchar.c",
-"musl/src/stdio/ofl.c",
-"musl/src/stdio/ofl_add.c",
-"musl/src/stdio/open_memstream.c",
-"musl/src/stdio/open_wmemstream.c",
-"musl/src/stdio/pclose.c",
-"musl/src/stdio/perror.c",
-"musl/src/stdio/popen.c",
-"musl/src/stdio/printf.c",
-"musl/src/stdio/putc.c",
-"musl/src/stdio/putc_unlocked.c",
-"musl/src/stdio/putchar.c",
-"musl/src/stdio/putchar_unlocked.c",
-"musl/src/stdio/puts.c",
-"musl/src/stdio/putw.c",
-"musl/src/stdio/putwc.c",
-"musl/src/stdio/putwchar.c",
-"musl/src/stdio/remove.c",
-"musl/src/stdio/rename.c",
-"musl/src/stdio/rewind.c",
-"musl/src/stdio/scanf.c",
-"musl/src/stdio/setbuf.c",
-"musl/src/stdio/setbuffer.c",
-"musl/src/stdio/setlinebuf.c",
-"musl/src/stdio/setvbuf.c",
-"musl/src/stdio/snprintf.c",
-"musl/src/stdio/sprintf.c",
-"musl/src/stdio/sscanf.c",
-"musl/src/stdio/stderr.c",
-"musl/src/stdio/stdin.c",
-"musl/src/stdio/stdout.c",
-"musl/src/stdio/swprintf.c",
-"musl/src/stdio/swscanf.c",
-"musl/src/stdio/tempnam.c",
-"musl/src/stdio/tmpfile.c",
-"musl/src/stdio/tmpnam.c",
-"musl/src/stdio/ungetc.c",
-"musl/src/stdio/ungetwc.c",
-"musl/src/stdio/vasprintf.c",
-"musl/src/stdio/vdprintf.c",
-"musl/src/stdio/vfprintf.c",
-"musl/src/stdio/vfscanf.c",
-"musl/src/stdio/vfwprintf.c",
-"musl/src/stdio/vfwscanf.c",
-"musl/src/stdio/vprintf.c",
-"musl/src/stdio/vscanf.c",
-"musl/src/stdio/vsnprintf.c",
-"musl/src/stdio/vsprintf.c",
-"musl/src/stdio/vsscanf.c",
-"musl/src/stdio/vswprintf.c",
-"musl/src/stdio/vswscanf.c",
-"musl/src/stdio/vwprintf.c",
-"musl/src/stdio/vwscanf.c",
-"musl/src/stdio/wprintf.c",
-"musl/src/stdio/wscanf.c",
-"musl/src/stdlib/abs.c",
-"musl/src/stdlib/atof.c",
-"musl/src/stdlib/atoi.c",
-"musl/src/stdlib/atol.c",
-"musl/src/stdlib/atoll.c",
-"musl/src/stdlib/bsearch.c",
-"musl/src/stdlib/div.c",
-"musl/src/stdlib/ecvt.c",
-"musl/src/stdlib/fcvt.c",
-"musl/src/stdlib/gcvt.c",
-"musl/src/stdlib/imaxabs.c",
-"musl/src/stdlib/imaxdiv.c",
-"musl/src/stdlib/labs.c",
-"musl/src/stdlib/ldiv.c",
-"musl/src/stdlib/llabs.c",
-"musl/src/stdlib/lldiv.c",
-"musl/src/stdlib/qsort.c",
-"musl/src/stdlib/strtod.c",
-"musl/src/stdlib/strtol.c",
-"musl/src/stdlib/wcstod.c",
-"musl/src/stdlib/wcstol.c",
-"musl/src/string/arm/__aeabi_memcpy.s",
-"musl/src/string/arm/__aeabi_memset.s",
-"musl/src/string/arm/memcpy.c",
-"musl/src/string/arm/memcpy_le.S",
-"musl/src/string/bcmp.c",
-"musl/src/string/bcopy.c",
-"musl/src/string/bzero.c",
-"musl/src/string/explicit_bzero.c",
-"musl/src/string/i386/memcpy.s",
-"musl/src/string/i386/memmove.s",
-"musl/src/string/i386/memset.s",
-"musl/src/string/index.c",
-"musl/src/string/memccpy.c",
-"musl/src/string/memchr.c",
-"musl/src/string/memcmp.c",
-"musl/src/string/memcpy.c",
-"musl/src/string/memmem.c",
-"musl/src/string/memmove.c",
-"musl/src/string/mempcpy.c",
-"musl/src/string/memrchr.c",
-"musl/src/string/memset.c",
-"musl/src/string/rindex.c",
-"musl/src/string/stpcpy.c",
-"musl/src/string/stpncpy.c",
-"musl/src/string/strcasecmp.c",
-"musl/src/string/strcasestr.c",
-"musl/src/string/strcat.c",
-"musl/src/string/strchr.c",
-"musl/src/string/strchrnul.c",
-"musl/src/string/strcmp.c",
-"musl/src/string/strcpy.c",
-"musl/src/string/strcspn.c",
-"musl/src/string/strdup.c",
-"musl/src/string/strerror_r.c",
-"musl/src/string/strlcat.c",
-"musl/src/string/strlcpy.c",
-"musl/src/string/strlen.c",
-"musl/src/string/strncasecmp.c",
-"musl/src/string/strncat.c",
-"musl/src/string/strncmp.c",
-"musl/src/string/strncpy.c",
-"musl/src/string/strndup.c",
-"musl/src/string/strnlen.c",
-"musl/src/string/strpbrk.c",
-"musl/src/string/strrchr.c",
-"musl/src/string/strsep.c",
-"musl/src/string/strsignal.c",
-"musl/src/string/strspn.c",
-"musl/src/string/strstr.c",
-"musl/src/string/strtok.c",
-"musl/src/string/strtok_r.c",
-"musl/src/string/strverscmp.c",
-"musl/src/string/swab.c",
-"musl/src/string/wcpcpy.c",
-"musl/src/string/wcpncpy.c",
-"musl/src/string/wcscasecmp.c",
-"musl/src/string/wcscasecmp_l.c",
-"musl/src/string/wcscat.c",
-"musl/src/string/wcschr.c",
-"musl/src/string/wcscmp.c",
-"musl/src/string/wcscpy.c",
-"musl/src/string/wcscspn.c",
-"musl/src/string/wcsdup.c",
-"musl/src/string/wcslen.c",
-"musl/src/string/wcsncasecmp.c",
-"musl/src/string/wcsncasecmp_l.c",
-"musl/src/string/wcsncat.c",
-"musl/src/string/wcsncmp.c",
-"musl/src/string/wcsncpy.c",
-"musl/src/string/wcsnlen.c",
-"musl/src/string/wcspbrk.c",
-"musl/src/string/wcsrchr.c",
-"musl/src/string/wcsspn.c",
-"musl/src/string/wcsstr.c",
-"musl/src/string/wcstok.c",
-"musl/src/string/wcswcs.c",
-"musl/src/string/wmemchr.c",
-"musl/src/string/wmemcmp.c",
-"musl/src/string/wmemcpy.c",
-"musl/src/string/wmemmove.c",
-"musl/src/string/wmemset.c",
-"musl/src/string/x86_64/memcpy.s",
-"musl/src/string/x86_64/memmove.s",
-"musl/src/string/x86_64/memset.s",
-"musl/src/temp/__randname.c",
-"musl/src/temp/mkdtemp.c",
-"musl/src/temp/mkostemp.c",
-"musl/src/temp/mkostemps.c",
-"musl/src/temp/mkstemp.c",
-"musl/src/temp/mkstemps.c",
-"musl/src/temp/mktemp.c",
-"musl/src/termios/cfgetospeed.c",
-"musl/src/termios/cfmakeraw.c",
-"musl/src/termios/cfsetospeed.c",
-"musl/src/termios/tcdrain.c",
-"musl/src/termios/tcflow.c",
-"musl/src/termios/tcflush.c",
-"musl/src/termios/tcgetattr.c",
-"musl/src/termios/tcgetsid.c",
-"musl/src/termios/tcsendbreak.c",
-"musl/src/termios/tcsetattr.c",
-"musl/src/thread/__lock.c",
-"musl/src/thread/__set_thread_area.c",
-"musl/src/thread/__syscall_cp.c",
-"musl/src/thread/__timedwait.c",
-"musl/src/thread/__tls_get_addr.c",
-"musl/src/thread/__unmapself.c",
-"musl/src/thread/__wait.c",
-"musl/src/thread/aarch64/__set_thread_area.s",
-"musl/src/thread/aarch64/__unmapself.s",
-"musl/src/thread/aarch64/clone.s",
-"musl/src/thread/aarch64/syscall_cp.s",
-"musl/src/thread/arm/__aeabi_read_tp.s",
-"musl/src/thread/arm/__set_thread_area.c",
-"musl/src/thread/arm/__unmapself.s",
-"musl/src/thread/arm/atomics.s",
-"musl/src/thread/arm/clone.s",
-"musl/src/thread/arm/syscall_cp.s",
-"musl/src/thread/call_once.c",
-"musl/src/thread/clone.c",
-"musl/src/thread/cnd_broadcast.c",
-"musl/src/thread/cnd_destroy.c",
-"musl/src/thread/cnd_init.c",
-"musl/src/thread/cnd_signal.c",
-"musl/src/thread/cnd_timedwait.c",
-"musl/src/thread/cnd_wait.c",
-"musl/src/thread/default_attr.c",
-"musl/src/thread/i386/__set_thread_area.s",
-"musl/src/thread/i386/__unmapself.s",
-"musl/src/thread/i386/clone.s",
-"musl/src/thread/i386/syscall_cp.s",
-"musl/src/thread/i386/tls.s",
-"musl/src/thread/lock_ptc.c",
-"musl/src/thread/m68k/__m68k_read_tp.s",
-"musl/src/thread/m68k/clone.s",
-"musl/src/thread/m68k/syscall_cp.s",
-"musl/src/thread/microblaze/__set_thread_area.s",
-"musl/src/thread/microblaze/__unmapself.s",
-"musl/src/thread/microblaze/clone.s",
-"musl/src/thread/microblaze/syscall_cp.s",
-"musl/src/thread/mips/__unmapself.s",
-"musl/src/thread/mips/clone.s",
-"musl/src/thread/mips/syscall_cp.s",
-"musl/src/thread/mips64/__unmapself.s",
-"musl/src/thread/mips64/clone.s",
-"musl/src/thread/mips64/syscall_cp.s",
-"musl/src/thread/mipsn32/__unmapself.s",
-"musl/src/thread/mipsn32/clone.s",
-"musl/src/thread/mipsn32/syscall_cp.s",
-"musl/src/thread/mtx_destroy.c",
-"musl/src/thread/mtx_init.c",
-"musl/src/thread/mtx_lock.c",
-"musl/src/thread/mtx_timedlock.c",
-"musl/src/thread/mtx_trylock.c",
-"musl/src/thread/mtx_unlock.c",
-"musl/src/thread/or1k/__set_thread_area.s",
-"musl/src/thread/or1k/__unmapself.s",
-"musl/src/thread/or1k/clone.s",
-"musl/src/thread/or1k/syscall_cp.s",
-"musl/src/thread/powerpc/__set_thread_area.s",
-"musl/src/thread/powerpc/__unmapself.s",
-"musl/src/thread/powerpc/clone.s",
-"musl/src/thread/powerpc/syscall_cp.s",
-"musl/src/thread/powerpc64/__set_thread_area.s",
-"musl/src/thread/powerpc64/__unmapself.s",
-"musl/src/thread/powerpc64/clone.s",
-"musl/src/thread/powerpc64/syscall_cp.s",
-"musl/src/thread/pthread_atfork.c",
-"musl/src/thread/pthread_attr_destroy.c",
-"musl/src/thread/pthread_attr_get.c",
-"musl/src/thread/pthread_attr_init.c",
-"musl/src/thread/pthread_attr_setdetachstate.c",
-"musl/src/thread/pthread_attr_setguardsize.c",
-"musl/src/thread/pthread_attr_setinheritsched.c",
-"musl/src/thread/pthread_attr_setschedparam.c",
-"musl/src/thread/pthread_attr_setschedpolicy.c",
-"musl/src/thread/pthread_attr_setscope.c",
-"musl/src/thread/pthread_attr_setstack.c",
-"musl/src/thread/pthread_attr_setstacksize.c",
-"musl/src/thread/pthread_barrier_destroy.c",
-"musl/src/thread/pthread_barrier_init.c",
-"musl/src/thread/pthread_barrier_wait.c",
-"musl/src/thread/pthread_barrierattr_destroy.c",
-"musl/src/thread/pthread_barrierattr_init.c",
-"musl/src/thread/pthread_barrierattr_setpshared.c",
-"musl/src/thread/pthread_cancel.c",
-"musl/src/thread/pthread_cleanup_push.c",
-"musl/src/thread/pthread_cond_broadcast.c",
-"musl/src/thread/pthread_cond_destroy.c",
-"musl/src/thread/pthread_cond_init.c",
-"musl/src/thread/pthread_cond_signal.c",
-"musl/src/thread/pthread_cond_timedwait.c",
-"musl/src/thread/pthread_cond_wait.c",
-"musl/src/thread/pthread_condattr_destroy.c",
-"musl/src/thread/pthread_condattr_init.c",
-"musl/src/thread/pthread_condattr_setclock.c",
-"musl/src/thread/pthread_condattr_setpshared.c",
-"musl/src/thread/pthread_create.c",
-"musl/src/thread/pthread_detach.c",
-"musl/src/thread/pthread_equal.c",
-"musl/src/thread/pthread_getattr_np.c",
-"musl/src/thread/pthread_getconcurrency.c",
-"musl/src/thread/pthread_getcpuclockid.c",
-"musl/src/thread/pthread_getschedparam.c",
-"musl/src/thread/pthread_getspecific.c",
-"musl/src/thread/pthread_join.c",
-"musl/src/thread/pthread_key_create.c",
-"musl/src/thread/pthread_kill.c",
-"musl/src/thread/pthread_mutex_consistent.c",
-"musl/src/thread/pthread_mutex_destroy.c",
-"musl/src/thread/pthread_mutex_getprioceiling.c",
-"musl/src/thread/pthread_mutex_init.c",
-"musl/src/thread/pthread_mutex_lock.c",
-"musl/src/thread/pthread_mutex_setprioceiling.c",
-"musl/src/thread/pthread_mutex_timedlock.c",
-"musl/src/thread/pthread_mutex_trylock.c",
-"musl/src/thread/pthread_mutex_unlock.c",
-"musl/src/thread/pthread_mutexattr_destroy.c",
-"musl/src/thread/pthread_mutexattr_init.c",
-"musl/src/thread/pthread_mutexattr_setprotocol.c",
-"musl/src/thread/pthread_mutexattr_setpshared.c",
-"musl/src/thread/pthread_mutexattr_setrobust.c",
-"musl/src/thread/pthread_mutexattr_settype.c",
-"musl/src/thread/pthread_once.c",
-"musl/src/thread/pthread_rwlock_destroy.c",
-"musl/src/thread/pthread_rwlock_init.c",
-"musl/src/thread/pthread_rwlock_rdlock.c",
-"musl/src/thread/pthread_rwlock_timedrdlock.c",
-"musl/src/thread/pthread_rwlock_timedwrlock.c",
-"musl/src/thread/pthread_rwlock_tryrdlock.c",
-"musl/src/thread/pthread_rwlock_trywrlock.c",
-"musl/src/thread/pthread_rwlock_unlock.c",
-"musl/src/thread/pthread_rwlock_wrlock.c",
-"musl/src/thread/pthread_rwlockattr_destroy.c",
-"musl/src/thread/pthread_rwlockattr_init.c",
-"musl/src/thread/pthread_rwlockattr_setpshared.c",
-"musl/src/thread/pthread_self.c",
-"musl/src/thread/pthread_setattr_default_np.c",
-"musl/src/thread/pthread_setcancelstate.c",
-"musl/src/thread/pthread_setcanceltype.c",
-"musl/src/thread/pthread_setconcurrency.c",
-"musl/src/thread/pthread_setname_np.c",
-"musl/src/thread/pthread_setschedparam.c",
-"musl/src/thread/pthread_setschedprio.c",
-"musl/src/thread/pthread_setspecific.c",
-"musl/src/thread/pthread_sigmask.c",
-"musl/src/thread/pthread_spin_destroy.c",
-"musl/src/thread/pthread_spin_init.c",
-"musl/src/thread/pthread_spin_lock.c",
-"musl/src/thread/pthread_spin_trylock.c",
-"musl/src/thread/pthread_spin_unlock.c",
-"musl/src/thread/pthread_testcancel.c",
-"musl/src/thread/riscv64/__set_thread_area.s",
-"musl/src/thread/riscv64/__unmapself.s",
-"musl/src/thread/riscv64/clone.s",
-"musl/src/thread/riscv64/syscall_cp.s",
-"musl/src/thread/s390x/__set_thread_area.s",
-"musl/src/thread/s390x/__tls_get_offset.s",
-"musl/src/thread/s390x/__unmapself.s",
-"musl/src/thread/s390x/clone.s",
-"musl/src/thread/s390x/syscall_cp.s",
-"musl/src/thread/sem_destroy.c",
-"musl/src/thread/sem_getvalue.c",
-"musl/src/thread/sem_init.c",
-"musl/src/thread/sem_open.c",
-"musl/src/thread/sem_post.c",
-"musl/src/thread/sem_timedwait.c",
-"musl/src/thread/sem_trywait.c",
-"musl/src/thread/sem_unlink.c",
-"musl/src/thread/sem_wait.c",
-"musl/src/thread/sh/__set_thread_area.c",
-"musl/src/thread/sh/__unmapself.c",
-"musl/src/thread/sh/__unmapself_mmu.s",
-"musl/src/thread/sh/atomics.s",
-"musl/src/thread/sh/clone.s",
-"musl/src/thread/sh/syscall_cp.s",
-"musl/src/thread/synccall.c",
-"musl/src/thread/syscall_cp.c",
-"musl/src/thread/thrd_create.c",
-"musl/src/thread/thrd_exit.c",
-"musl/src/thread/thrd_join.c",
-"musl/src/thread/thrd_sleep.c",
-"musl/src/thread/thrd_yield.c",
-"musl/src/thread/tls.c",
-"musl/src/thread/tss_create.c",
-"musl/src/thread/tss_delete.c",
-"musl/src/thread/tss_set.c",
-"musl/src/thread/vmlock.c",
-"musl/src/thread/x32/__set_thread_area.s",
-"musl/src/thread/x32/__unmapself.s",
-"musl/src/thread/x32/clone.s",
-"musl/src/thread/x32/syscall_cp.s",
-"musl/src/thread/x86_64/__set_thread_area.s",
-"musl/src/thread/x86_64/__unmapself.s",
-"musl/src/thread/x86_64/clone.s",
-"musl/src/thread/x86_64/syscall_cp.s",
-"musl/src/time/__map_file.c",
-"musl/src/time/__month_to_secs.c",
-"musl/src/time/__secs_to_tm.c",
-"musl/src/time/__tm_to_secs.c",
-"musl/src/time/__tz.c",
-"musl/src/time/__year_to_secs.c",
-"musl/src/time/asctime.c",
-"musl/src/time/asctime_r.c",
-"musl/src/time/clock.c",
-"musl/src/time/clock_getcpuclockid.c",
-"musl/src/time/clock_getres.c",
-"musl/src/time/clock_gettime.c",
-"musl/src/time/clock_nanosleep.c",
-"musl/src/time/clock_settime.c",
-"musl/src/time/ctime.c",
-"musl/src/time/ctime_r.c",
-"musl/src/time/difftime.c",
-"musl/src/time/ftime.c",
-"musl/src/time/getdate.c",
-"musl/src/time/gettimeofday.c",
-"musl/src/time/gmtime.c",
-"musl/src/time/gmtime_r.c",
-"musl/src/time/localtime.c",
-"musl/src/time/localtime_r.c",
-"musl/src/time/mktime.c",
-"musl/src/time/nanosleep.c",
-"musl/src/time/strftime.c",
-"musl/src/time/strptime.c",
-"musl/src/time/time.c",
-"musl/src/time/timegm.c",
-"musl/src/time/timer_create.c",
-"musl/src/time/timer_delete.c",
-"musl/src/time/timer_getoverrun.c",
-"musl/src/time/timer_gettime.c",
-"musl/src/time/timer_settime.c",
-"musl/src/time/times.c",
-"musl/src/time/timespec_get.c",
-"musl/src/time/utime.c",
-"musl/src/time/wcsftime.c",
-"musl/src/unistd/_exit.c",
-"musl/src/unistd/access.c",
-"musl/src/unistd/acct.c",
-"musl/src/unistd/alarm.c",
-"musl/src/unistd/chdir.c",
-"musl/src/unistd/chown.c",
-"musl/src/unistd/close.c",
-"musl/src/unistd/ctermid.c",
-"musl/src/unistd/dup.c",
-"musl/src/unistd/dup2.c",
-"musl/src/unistd/dup3.c",
-"musl/src/unistd/faccessat.c",
-"musl/src/unistd/fchdir.c",
-"musl/src/unistd/fchown.c",
-"musl/src/unistd/fchownat.c",
-"musl/src/unistd/fdatasync.c",
-"musl/src/unistd/fsync.c",
-"musl/src/unistd/ftruncate.c",
-"musl/src/unistd/getcwd.c",
-"musl/src/unistd/getegid.c",
-"musl/src/unistd/geteuid.c",
-"musl/src/unistd/getgid.c",
-"musl/src/unistd/getgroups.c",
-"musl/src/unistd/gethostname.c",
-"musl/src/unistd/getlogin.c",
-"musl/src/unistd/getlogin_r.c",
-"musl/src/unistd/getpgid.c",
-"musl/src/unistd/getpgrp.c",
-"musl/src/unistd/getpid.c",
-"musl/src/unistd/getppid.c",
-"musl/src/unistd/getsid.c",
-"musl/src/unistd/getuid.c",
-"musl/src/unistd/isatty.c",
-"musl/src/unistd/lchown.c",
-"musl/src/unistd/link.c",
-"musl/src/unistd/linkat.c",
-"musl/src/unistd/lseek.c",
-"musl/src/unistd/mips/pipe.s",
-"musl/src/unistd/mips64/pipe.s",
-"musl/src/unistd/mipsn32/lseek.c",
-"musl/src/unistd/mipsn32/pipe.s",
-"musl/src/unistd/nice.c",
-"musl/src/unistd/pause.c",
-"musl/src/unistd/pipe.c",
-"musl/src/unistd/pipe2.c",
-"musl/src/unistd/posix_close.c",
-"musl/src/unistd/pread.c",
-"musl/src/unistd/preadv.c",
-"musl/src/unistd/pwrite.c",
-"musl/src/unistd/pwritev.c",
-"musl/src/unistd/read.c",
-"musl/src/unistd/readlink.c",
-"musl/src/unistd/readlinkat.c",
-"musl/src/unistd/readv.c",
-"musl/src/unistd/renameat.c",
-"musl/src/unistd/rmdir.c",
-"musl/src/unistd/setegid.c",
-"musl/src/unistd/seteuid.c",
-"musl/src/unistd/setgid.c",
-"musl/src/unistd/setpgid.c",
-"musl/src/unistd/setpgrp.c",
-"musl/src/unistd/setregid.c",
-"musl/src/unistd/setresgid.c",
-"musl/src/unistd/setresuid.c",
-"musl/src/unistd/setreuid.c",
-"musl/src/unistd/setsid.c",
-"musl/src/unistd/setuid.c",
-"musl/src/unistd/setxid.c",
-"musl/src/unistd/sh/pipe.s",
-"musl/src/unistd/sleep.c",
-"musl/src/unistd/symlink.c",
-"musl/src/unistd/symlinkat.c",
-"musl/src/unistd/sync.c",
-"musl/src/unistd/tcgetpgrp.c",
-"musl/src/unistd/tcsetpgrp.c",
-"musl/src/unistd/truncate.c",
-"musl/src/unistd/ttyname.c",
-"musl/src/unistd/ttyname_r.c",
-"musl/src/unistd/ualarm.c",
-"musl/src/unistd/unlink.c",
-"musl/src/unistd/unlinkat.c",
-"musl/src/unistd/usleep.c",
-"musl/src/unistd/write.c",
-"musl/src/unistd/writev.c",
-"musl/src/unistd/x32/lseek.c",
-};
-static const char *ZIG_MUSL_COMPAT_TIME32_FILES[] = {
-"musl/compat/time32/__xstat.c",
-"musl/compat/time32/adjtime32.c",
-"musl/compat/time32/adjtimex_time32.c",
-"musl/compat/time32/aio_suspend_time32.c",
-"musl/compat/time32/clock_adjtime32.c",
-"musl/compat/time32/clock_getres_time32.c",
-"musl/compat/time32/clock_gettime32.c",
-"musl/compat/time32/clock_nanosleep_time32.c",
-"musl/compat/time32/clock_settime32.c",
-"musl/compat/time32/cnd_timedwait_time32.c",
-"musl/compat/time32/ctime32.c",
-"musl/compat/time32/ctime32_r.c",
-"musl/compat/time32/difftime32.c",
-"musl/compat/time32/fstat_time32.c",
-"musl/compat/time32/fstatat_time32.c",
-"musl/compat/time32/ftime32.c",
-"musl/compat/time32/futimens_time32.c",
-"musl/compat/time32/futimes_time32.c",
-"musl/compat/time32/futimesat_time32.c",
-"musl/compat/time32/getitimer_time32.c",
-"musl/compat/time32/getrusage_time32.c",
-"musl/compat/time32/gettimeofday_time32.c",
-"musl/compat/time32/gmtime32.c",
-"musl/compat/time32/gmtime32_r.c",
-"musl/compat/time32/localtime32.c",
-"musl/compat/time32/localtime32_r.c",
-"musl/compat/time32/lstat_time32.c",
-"musl/compat/time32/lutimes_time32.c",
-"musl/compat/time32/mktime32.c",
-"musl/compat/time32/mq_timedreceive_time32.c",
-"musl/compat/time32/mq_timedsend_time32.c",
-"musl/compat/time32/mtx_timedlock_time32.c",
-"musl/compat/time32/nanosleep_time32.c",
-"musl/compat/time32/ppoll_time32.c",
-"musl/compat/time32/pselect_time32.c",
-"musl/compat/time32/pthread_cond_timedwait_time32.c",
-"musl/compat/time32/pthread_mutex_timedlock_time32.c",
-"musl/compat/time32/pthread_rwlock_timedrdlock_time32.c",
-"musl/compat/time32/pthread_rwlock_timedwrlock_time32.c",
-"musl/compat/time32/pthread_timedjoin_np_time32.c",
-"musl/compat/time32/recvmmsg_time32.c",
-"musl/compat/time32/sched_rr_get_interval_time32.c",
-"musl/compat/time32/select_time32.c",
-"musl/compat/time32/sem_timedwait_time32.c",
-"musl/compat/time32/semtimedop_time32.c",
-"musl/compat/time32/setitimer_time32.c",
-"musl/compat/time32/settimeofday_time32.c",
-"musl/compat/time32/sigtimedwait_time32.c",
-"musl/compat/time32/stat_time32.c",
-"musl/compat/time32/stime32.c",
-"musl/compat/time32/thrd_sleep_time32.c",
-"musl/compat/time32/time32.c",
-"musl/compat/time32/time32gm.c",
-"musl/compat/time32/timer_gettime32.c",
-"musl/compat/time32/timer_settime32.c",
-"musl/compat/time32/timerfd_gettime32.c",
-"musl/compat/time32/timerfd_settime32.c",
-"musl/compat/time32/timespec_get_time32.c",
-"musl/compat/time32/utime_time32.c",
-"musl/compat/time32/utimensat_time32.c",
-"musl/compat/time32/utimes_time32.c",
-"musl/compat/time32/wait3_time32.c",
-"musl/compat/time32/wait4_time32.c",
-};
-static const char *ZIG_LIBCXXABI_FILES[] = {
-"src/abort_message.cpp",
-"src/cxa_aux_runtime.cpp",
-"src/cxa_default_handlers.cpp",
-"src/cxa_demangle.cpp",
-"src/cxa_exception.cpp",
-"src/cxa_exception_storage.cpp",
-"src/cxa_guard.cpp",
-"src/cxa_handlers.cpp",
-"src/cxa_noexception.cpp",
-"src/cxa_personality.cpp",
-"src/cxa_thread_atexit.cpp",
-"src/cxa_vector.cpp",
-"src/cxa_virtual.cpp",
-"src/fallback_malloc.cpp",
-"src/private_typeinfo.cpp",
-"src/stdlib_exception.cpp",
-"src/stdlib_new_delete.cpp",
-"src/stdlib_stdexcept.cpp",
-"src/stdlib_typeinfo.cpp",
-};
-static const char *ZIG_LIBCXX_FILES[] = {
-"src/algorithm.cpp",
-"src/any.cpp",
-"src/atomic.cpp",
-"src/barrier.cpp",
-"src/bind.cpp",
-"src/charconv.cpp",
-"src/chrono.cpp",
-"src/condition_variable.cpp",
-"src/condition_variable_destructor.cpp",
-"src/debug.cpp",
-"src/exception.cpp",
-"src/experimental/memory_resource.cpp",
-"src/filesystem/directory_iterator.cpp",
-"src/filesystem/int128_builtins.cpp",
-"src/filesystem/operations.cpp",
-"src/functional.cpp",
-"src/future.cpp",
-"src/hash.cpp",
-"src/ios.cpp",
-"src/iostream.cpp",
-"src/locale.cpp",
-"src/memory.cpp",
-"src/mutex.cpp",
-"src/mutex_destructor.cpp",
-"src/new.cpp",
-"src/optional.cpp",
-"src/random.cpp",
-"src/random_shuffle.cpp",
-"src/regex.cpp",
-"src/shared_mutex.cpp",
-"src/stdexcept.cpp",
-"src/string.cpp",
-"src/strstream.cpp",
-"src/support/solaris/xlocale.cpp",
-"src/support/win32/locale_win32.cpp",
-"src/support/win32/support.cpp",
-"src/support/win32/thread_win32.cpp",
-"src/system_error.cpp",
-"src/thread.cpp",
-"src/typeinfo.cpp",
-"src/utility.cpp",
-"src/valarray.cpp",
-"src/variant.cpp",
-"src/vector.cpp",
-};
-#endif
diff --git a/src/introspect.zig b/src/introspect.zig
new file mode 100644
index 0000000000..b75bf8f4b8
--- /dev/null
+++ b/src/introspect.zig
@@ -0,0 +1,75 @@
+const std = @import("std");
+const mem = std.mem;
+const fs = std.fs;
+const Compilation = @import("Compilation.zig");
+
+/// Returns the sub_path that worked, or `null` if none did.
+/// The path of the returned Directory is relative to `base`.
+/// The handle of the returned Directory is open.
+fn testZigInstallPrefix(base_dir: fs.Dir) ?Compilation.Directory {
+ const test_index_file = "std" ++ fs.path.sep_str ++ "std.zig";
+
+ zig_dir: {
+ // Try lib/zig/std/std.zig
+ const lib_zig = "lib" ++ fs.path.sep_str ++ "zig";
+ var test_zig_dir = base_dir.openDir(lib_zig, .{}) catch break :zig_dir;
+ const file = test_zig_dir.openFile(test_index_file, .{}) catch {
+ test_zig_dir.close();
+ break :zig_dir;
+ };
+ file.close();
+ return Compilation.Directory{ .handle = test_zig_dir, .path = lib_zig };
+ }
+
+ // Try lib/std/std.zig
+ var test_zig_dir = base_dir.openDir("lib", .{}) catch return null;
+ const file = test_zig_dir.openFile(test_index_file, .{}) catch {
+ test_zig_dir.close();
+ return null;
+ };
+ file.close();
+ return Compilation.Directory{ .handle = test_zig_dir, .path = "lib" };
+}
+
+/// Both the directory handle and the path are newly allocated resources which the caller now owns.
+pub fn findZigLibDir(gpa: *mem.Allocator) !Compilation.Directory {
+ const self_exe_path = try fs.selfExePathAlloc(gpa);
+ defer gpa.free(self_exe_path);
+
+ return findZigLibDirFromSelfExe(gpa, self_exe_path);
+}
+
+/// Both the directory handle and the path are newly allocated resources which the caller now owns.
+pub fn findZigLibDirFromSelfExe(
+ allocator: *mem.Allocator,
+ self_exe_path: []const u8,
+) error{ OutOfMemory, FileNotFound }!Compilation.Directory {
+ const cwd = fs.cwd();
+ var cur_path: []const u8 = self_exe_path;
+ while (fs.path.dirname(cur_path)) |dirname| : (cur_path = dirname) {
+ var base_dir = cwd.openDir(dirname, .{}) catch continue;
+ defer base_dir.close();
+
+ const sub_directory = testZigInstallPrefix(base_dir) orelse continue;
+ return Compilation.Directory{
+ .handle = sub_directory.handle,
+ .path = try fs.path.join(allocator, &[_][]const u8{ dirname, sub_directory.path.? }),
+ };
+ }
+ return error.FileNotFound;
+}
+
+/// Caller owns returned memory.
+pub fn resolveGlobalCacheDir(allocator: *mem.Allocator) ![]u8 {
+ const appname = "zig";
+
+ if (std.Target.current.os.tag != .windows) {
+ if (std.os.getenv("XDG_CACHE_HOME")) |cache_root| {
+ return fs.path.join(allocator, &[_][]const u8{ cache_root, appname });
+ } else if (std.os.getenv("HOME")) |home| {
+ return fs.path.join(allocator, &[_][]const u8{ home, ".cache", appname });
+ }
+ }
+
+ return fs.getAppDataDir(allocator, appname);
+}
diff --git a/src-self-hosted/ir.zig b/src/ir.zig
similarity index 100%
rename from src-self-hosted/ir.zig
rename to src/ir.zig
diff --git a/src-self-hosted/libc_installation.zig b/src/libc_installation.zig
similarity index 99%
rename from src-self-hosted/libc_installation.zig
rename to src/libc_installation.zig
index fa2ef30ccd..535892ce74 100644
--- a/src-self-hosted/libc_installation.zig
+++ b/src/libc_installation.zig
@@ -4,6 +4,7 @@ const Target = std.Target;
const fs = std.fs;
const Allocator = std.mem.Allocator;
const Batch = std.event.Batch;
+const build_options = @import("build_options");
const is_darwin = Target.current.isDarwin();
const is_windows = Target.current.os.tag == .windows;
@@ -13,6 +14,8 @@ const log = std.log.scoped(.libc_installation);
usingnamespace @import("windows_sdk.zig");
+// TODO https://github.com/ziglang/zig/issues/6345
+
/// See the render function implementation for documentation of the fields.
pub const LibCInstallation = struct {
include_dir: ?[]const u8 = null,
@@ -168,6 +171,8 @@ pub const LibCInstallation = struct {
var self: LibCInstallation = .{};
if (is_windows) {
+ if (!build_options.have_llvm)
+ return error.WindowsSdkNotFound;
var sdk: *ZigWindowsSDK = undefined;
switch (zig_find_windows_sdk(&sdk)) {
.None => {
diff --git a/src/libcxx.zig b/src/libcxx.zig
new file mode 100644
index 0000000000..b76c8f1163
--- /dev/null
+++ b/src/libcxx.zig
@@ -0,0 +1,319 @@
+const std = @import("std");
+const path = std.fs.path;
+const assert = std.debug.assert;
+
+const target_util = @import("target.zig");
+const Compilation = @import("Compilation.zig");
+const build_options = @import("build_options");
+const trace = @import("tracy.zig").trace;
+
+const libcxxabi_files = [_][]const u8{
+ "src/abort_message.cpp",
+ "src/cxa_aux_runtime.cpp",
+ "src/cxa_default_handlers.cpp",
+ "src/cxa_demangle.cpp",
+ "src/cxa_exception.cpp",
+ "src/cxa_exception_storage.cpp",
+ "src/cxa_guard.cpp",
+ "src/cxa_handlers.cpp",
+ "src/cxa_noexception.cpp",
+ "src/cxa_personality.cpp",
+ "src/cxa_thread_atexit.cpp",
+ "src/cxa_vector.cpp",
+ "src/cxa_virtual.cpp",
+ "src/fallback_malloc.cpp",
+ "src/private_typeinfo.cpp",
+ "src/stdlib_exception.cpp",
+ "src/stdlib_new_delete.cpp",
+ "src/stdlib_stdexcept.cpp",
+ "src/stdlib_typeinfo.cpp",
+};
+
+const libcxx_files = [_][]const u8{
+ "src/algorithm.cpp",
+ "src/any.cpp",
+ "src/atomic.cpp",
+ "src/barrier.cpp",
+ "src/bind.cpp",
+ "src/charconv.cpp",
+ "src/chrono.cpp",
+ "src/condition_variable.cpp",
+ "src/condition_variable_destructor.cpp",
+ "src/debug.cpp",
+ "src/exception.cpp",
+ "src/experimental/memory_resource.cpp",
+ "src/filesystem/directory_iterator.cpp",
+ "src/filesystem/int128_builtins.cpp",
+ "src/filesystem/operations.cpp",
+ "src/functional.cpp",
+ "src/future.cpp",
+ "src/hash.cpp",
+ "src/ios.cpp",
+ "src/iostream.cpp",
+ "src/locale.cpp",
+ "src/memory.cpp",
+ "src/mutex.cpp",
+ "src/mutex_destructor.cpp",
+ "src/new.cpp",
+ "src/optional.cpp",
+ "src/random.cpp",
+ "src/random_shuffle.cpp",
+ "src/regex.cpp",
+ "src/shared_mutex.cpp",
+ "src/stdexcept.cpp",
+ "src/string.cpp",
+ "src/strstream.cpp",
+ "src/support/solaris/xlocale.cpp",
+ "src/support/win32/locale_win32.cpp",
+ "src/support/win32/support.cpp",
+ "src/support/win32/thread_win32.cpp",
+ "src/system_error.cpp",
+ "src/thread.cpp",
+ "src/typeinfo.cpp",
+ "src/utility.cpp",
+ "src/valarray.cpp",
+ "src/variant.cpp",
+ "src/vector.cpp",
+};
+
+pub fn buildLibCXX(comp: *Compilation) !void {
+ if (!build_options.have_llvm) {
+ return error.ZigCompilerNotBuiltWithLLVMExtensions;
+ }
+
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ const root_name = "c++";
+ const output_mode = .Lib;
+ const link_mode = .Static;
+ const target = comp.getTarget();
+ const basename = try std.zig.binNameAlloc(arena, .{
+ .root_name = root_name,
+ .target = target,
+ .output_mode = output_mode,
+ .link_mode = link_mode,
+ });
+
+ const emit_bin = Compilation.EmitLoc{
+ .directory = null, // Put it in the cache directory.
+ .basename = basename,
+ };
+
+ const cxxabi_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", "include" });
+ const cxx_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" });
+ var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena);
+ try c_source_files.ensureCapacity(libcxx_files.len);
+
+ for (libcxx_files) |cxx_src| {
+ var cflags = std.ArrayList([]const u8).init(arena);
+
+ if (target.os.tag == .windows) {
+ // Filesystem stuff isn't supported on Windows.
+ if (std.mem.startsWith(u8, cxx_src, "src/filesystem/"))
+ continue;
+ } else {
+ if (std.mem.startsWith(u8, cxx_src, "src/support/win32/"))
+ continue;
+ }
+
+ try cflags.append("-DNDEBUG");
+ try cflags.append("-D_LIBCPP_BUILDING_LIBRARY");
+ try cflags.append("-D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER");
+ try cflags.append("-DLIBCXX_BUILDING_LIBCXXABI");
+ try cflags.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS");
+ try cflags.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS");
+
+ if (target.abi.isMusl()) {
+ try cflags.append("-D_LIBCPP_HAS_MUSL_LIBC");
+ }
+
+ try cflags.append("-I");
+ try cflags.append(cxx_include_path);
+
+ try cflags.append("-I");
+ try cflags.append(cxxabi_include_path);
+
+ try cflags.append("-O3");
+ try cflags.append("-DNDEBUG");
+ if (target_util.supports_fpic(target)) {
+ try cflags.append("-fPIC");
+ }
+ try cflags.append("-nostdinc++");
+ try cflags.append("-fvisibility-inlines-hidden");
+ try cflags.append("-std=c++14");
+ try cflags.append("-Wno-user-defined-literals");
+
+ c_source_files.appendAssumeCapacity(.{
+ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", cxx_src }),
+ .extra_flags = cflags.items,
+ });
+ }
+
+ const sub_compilation = try Compilation.create(comp.gpa, .{
+ .local_cache_directory = comp.global_cache_directory,
+ .global_cache_directory = comp.global_cache_directory,
+ .zig_lib_directory = comp.zig_lib_directory,
+ .target = target,
+ .root_name = root_name,
+ .root_pkg = null,
+ .output_mode = output_mode,
+ .rand = comp.rand,
+ .libc_installation = comp.bin_file.options.libc_installation,
+ .emit_bin = emit_bin,
+ .optimize_mode = comp.bin_file.options.optimize_mode,
+ .link_mode = link_mode,
+ .want_sanitize_c = false,
+ .want_stack_check = false,
+ .want_valgrind = false,
+ .want_pic = comp.bin_file.options.pic,
+ .emit_h = null,
+ .strip = comp.bin_file.options.strip,
+ .is_native_os = comp.bin_file.options.is_native_os,
+ .self_exe_path = comp.self_exe_path,
+ .c_source_files = c_source_files.items,
+ .verbose_cc = comp.verbose_cc,
+ .verbose_link = comp.bin_file.options.verbose_link,
+ .verbose_tokenize = comp.verbose_tokenize,
+ .verbose_ast = comp.verbose_ast,
+ .verbose_ir = comp.verbose_ir,
+ .verbose_llvm_ir = comp.verbose_llvm_ir,
+ .verbose_cimport = comp.verbose_cimport,
+ .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
+ .clang_passthrough_mode = comp.clang_passthrough_mode,
+ .link_libc = true,
+ });
+ defer sub_compilation.destroy();
+
+ try sub_compilation.updateSubCompilation();
+
+ assert(comp.libcxx_static_lib == null);
+ comp.libcxx_static_lib = Compilation.CRTFile{
+ .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(
+ comp.gpa,
+ &[_][]const u8{basename},
+ ),
+ .lock = sub_compilation.bin_file.toOwnedLock(),
+ };
+}
+
+pub fn buildLibCXXABI(comp: *Compilation) !void {
+ if (!build_options.have_llvm) {
+ return error.ZigCompilerNotBuiltWithLLVMExtensions;
+ }
+
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ const root_name = "c++abi";
+ const output_mode = .Lib;
+ const link_mode = .Static;
+ const target = comp.getTarget();
+ const basename = try std.zig.binNameAlloc(arena, .{
+ .root_name = root_name,
+ .target = target,
+ .output_mode = output_mode,
+ .link_mode = link_mode,
+ });
+
+ const emit_bin = Compilation.EmitLoc{
+ .directory = null, // Put it in the cache directory.
+ .basename = basename,
+ };
+
+ const cxxabi_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", "include" });
+ const cxx_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" });
+
+ var c_source_files: [libcxxabi_files.len]Compilation.CSourceFile = undefined;
+ for (libcxxabi_files) |cxxabi_src, i| {
+ var cflags = std.ArrayList([]const u8).init(arena);
+
+ try cflags.append("-DHAVE___CXA_THREAD_ATEXIT_IMPL");
+ try cflags.append("-D_LIBCPP_DISABLE_EXTERN_TEMPLATE");
+ try cflags.append("-D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS");
+ try cflags.append("-D_LIBCXXABI_BUILDING_LIBRARY");
+ try cflags.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS");
+ try cflags.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS");
+
+ if (target.abi.isMusl()) {
+ try cflags.append("-D_LIBCPP_HAS_MUSL_LIBC");
+ }
+
+ try cflags.append("-I");
+ try cflags.append(cxxabi_include_path);
+
+ try cflags.append("-I");
+ try cflags.append(cxx_include_path);
+
+ try cflags.append("-O3");
+ try cflags.append("-DNDEBUG");
+ if (target_util.supports_fpic(target)) {
+ try cflags.append("-fPIC");
+ }
+ try cflags.append("-nostdinc++");
+ try cflags.append("-fstrict-aliasing");
+ try cflags.append("-funwind-tables");
+ try cflags.append("-D_DEBUG");
+ try cflags.append("-UNDEBUG");
+ try cflags.append("-std=c++11");
+
+ c_source_files[i] = .{
+ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", cxxabi_src }),
+ .extra_flags = cflags.items,
+ };
+ }
+
+ const sub_compilation = try Compilation.create(comp.gpa, .{
+ .local_cache_directory = comp.global_cache_directory,
+ .global_cache_directory = comp.global_cache_directory,
+ .zig_lib_directory = comp.zig_lib_directory,
+ .target = target,
+ .root_name = root_name,
+ .root_pkg = null,
+ .output_mode = output_mode,
+ .rand = comp.rand,
+ .libc_installation = comp.bin_file.options.libc_installation,
+ .emit_bin = emit_bin,
+ .optimize_mode = comp.bin_file.options.optimize_mode,
+ .link_mode = link_mode,
+ .want_sanitize_c = false,
+ .want_stack_check = false,
+ .want_valgrind = false,
+ .want_pic = comp.bin_file.options.pic,
+ .emit_h = null,
+ .strip = comp.bin_file.options.strip,
+ .is_native_os = comp.bin_file.options.is_native_os,
+ .self_exe_path = comp.self_exe_path,
+ .c_source_files = &c_source_files,
+ .verbose_cc = comp.verbose_cc,
+ .verbose_link = comp.bin_file.options.verbose_link,
+ .verbose_tokenize = comp.verbose_tokenize,
+ .verbose_ast = comp.verbose_ast,
+ .verbose_ir = comp.verbose_ir,
+ .verbose_llvm_ir = comp.verbose_llvm_ir,
+ .verbose_cimport = comp.verbose_cimport,
+ .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
+ .clang_passthrough_mode = comp.clang_passthrough_mode,
+ .link_libc = true,
+ });
+ defer sub_compilation.destroy();
+
+ try sub_compilation.updateSubCompilation();
+
+ assert(comp.libcxxabi_static_lib == null);
+ comp.libcxxabi_static_lib = Compilation.CRTFile{
+ .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(
+ comp.gpa,
+ &[_][]const u8{basename},
+ ),
+ .lock = sub_compilation.bin_file.toOwnedLock(),
+ };
+}
diff --git a/src/libunwind.zig b/src/libunwind.zig
new file mode 100644
index 0000000000..d47eed40dd
--- /dev/null
+++ b/src/libunwind.zig
@@ -0,0 +1,135 @@
+const std = @import("std");
+const path = std.fs.path;
+const assert = std.debug.assert;
+
+const target_util = @import("target.zig");
+const Compilation = @import("Compilation.zig");
+const build_options = @import("build_options");
+const trace = @import("tracy.zig").trace;
+
+pub fn buildStaticLib(comp: *Compilation) !void {
+ if (!build_options.have_llvm) {
+ return error.ZigCompilerNotBuiltWithLLVMExtensions;
+ }
+
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ const root_name = "unwind";
+ const output_mode = .Lib;
+ const link_mode = .Static;
+ const target = comp.getTarget();
+ const basename = try std.zig.binNameAlloc(arena, .{
+ .root_name = root_name,
+ .target = target,
+ .output_mode = output_mode,
+ .link_mode = link_mode,
+ });
+ const emit_bin = Compilation.EmitLoc{
+ .directory = null, // Put it in the cache directory.
+ .basename = basename,
+ };
+ const unwind_src_list = [_][]const u8{
+ "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "libunwind.cpp",
+ "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-EHABI.cpp",
+ "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-seh.cpp",
+ "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindLevel1.c",
+ "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindLevel1-gcc-ext.c",
+ "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-sjlj.c",
+ "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindRegistersRestore.S",
+ "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindRegistersSave.S",
+ };
+ var c_source_files: [unwind_src_list.len]Compilation.CSourceFile = undefined;
+ for (unwind_src_list) |unwind_src, i| {
+ var cflags = std.ArrayList([]const u8).init(arena);
+
+ switch (Compilation.classifyFileExt(unwind_src)) {
+ .c => {
+ try cflags.append("-std=c99");
+ },
+ .cpp => {
+ try cflags.appendSlice(&[_][]const u8{
+ "-fno-rtti",
+ "-I",
+ try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" }),
+ });
+ },
+ .assembly => {},
+ else => unreachable, // You can see the entire list of files just above.
+ }
+ try cflags.append("-I");
+ try cflags.append(try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libunwind", "include" }));
+ if (target_util.supports_fpic(target)) {
+ try cflags.append("-fPIC");
+ }
+ try cflags.append("-D_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS");
+ try cflags.append("-Wa,--noexecstack");
+
+ // This is intentionally always defined because the macro definition means, should it only
+ // build for the target specified by compiler defines. Since we pass -target the compiler
+ // defines will be correct.
+ try cflags.append("-D_LIBUNWIND_IS_NATIVE_ONLY");
+
+ if (comp.bin_file.options.optimize_mode == .Debug) {
+ try cflags.append("-D_DEBUG");
+ }
+ if (comp.bin_file.options.single_threaded) {
+ try cflags.append("-D_LIBUNWIND_HAS_NO_THREADS");
+ }
+ try cflags.append("-Wno-bitwise-conditional-parentheses");
+
+ c_source_files[i] = .{
+ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{unwind_src}),
+ .extra_flags = cflags.items,
+ };
+ }
+ const sub_compilation = try Compilation.create(comp.gpa, .{
+ .local_cache_directory = comp.global_cache_directory,
+ .global_cache_directory = comp.global_cache_directory,
+ .zig_lib_directory = comp.zig_lib_directory,
+ .target = target,
+ .root_name = root_name,
+ .root_pkg = null,
+ .output_mode = output_mode,
+ .rand = comp.rand,
+ .libc_installation = comp.bin_file.options.libc_installation,
+ .emit_bin = emit_bin,
+ .optimize_mode = comp.bin_file.options.optimize_mode,
+ .link_mode = link_mode,
+ .want_sanitize_c = false,
+ .want_stack_check = false,
+ .want_valgrind = false,
+ .want_pic = comp.bin_file.options.pic,
+ .emit_h = null,
+ .strip = comp.bin_file.options.strip,
+ .is_native_os = comp.bin_file.options.is_native_os,
+ .self_exe_path = comp.self_exe_path,
+ .c_source_files = &c_source_files,
+ .verbose_cc = comp.verbose_cc,
+ .verbose_link = comp.bin_file.options.verbose_link,
+ .verbose_tokenize = comp.verbose_tokenize,
+ .verbose_ast = comp.verbose_ast,
+ .verbose_ir = comp.verbose_ir,
+ .verbose_llvm_ir = comp.verbose_llvm_ir,
+ .verbose_cimport = comp.verbose_cimport,
+ .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
+ .clang_passthrough_mode = comp.clang_passthrough_mode,
+ .link_libc = true,
+ });
+ defer sub_compilation.destroy();
+
+ try sub_compilation.updateSubCompilation();
+
+ assert(comp.libunwind_static_lib == null);
+ comp.libunwind_static_lib = Compilation.CRTFile{
+ .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(
+ comp.gpa,
+ &[_][]const u8{basename},
+ ),
+ .lock = sub_compilation.bin_file.toOwnedLock(),
+ };
+}
diff --git a/src/link.cpp b/src/link.cpp
deleted file mode 100644
index b54fdf6b93..0000000000
--- a/src/link.cpp
+++ /dev/null
@@ -1,2984 +0,0 @@
-/*
- * Copyright (c) 2015 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#include "os.hpp"
-#include "config.h"
-#include "codegen.hpp"
-#include "analyze.hpp"
-#include "compiler.hpp"
-#include "install_files.h"
-#include "glibc.hpp"
-
-static const char *msvcrt_common_src[] = {
- "misc" OS_SEP "_create_locale.c",
- "misc" OS_SEP "_free_locale.c",
- "misc" OS_SEP "onexit_table.c",
- "misc" OS_SEP "register_tls_atexit.c",
- "stdio" OS_SEP "acrt_iob_func.c",
- "misc" OS_SEP "_configthreadlocale.c",
- "misc" OS_SEP "_get_current_locale.c",
- "misc" OS_SEP "invalid_parameter_handler.c",
- "misc" OS_SEP "output_format.c",
- "misc" OS_SEP "purecall.c",
- "secapi" OS_SEP "_access_s.c",
- "secapi" OS_SEP "_cgets_s.c",
- "secapi" OS_SEP "_cgetws_s.c",
- "secapi" OS_SEP "_chsize_s.c",
- "secapi" OS_SEP "_controlfp_s.c",
- "secapi" OS_SEP "_cprintf_s.c",
- "secapi" OS_SEP "_cprintf_s_l.c",
- "secapi" OS_SEP "_ctime32_s.c",
- "secapi" OS_SEP "_ctime64_s.c",
- "secapi" OS_SEP "_cwprintf_s.c",
- "secapi" OS_SEP "_cwprintf_s_l.c",
- "secapi" OS_SEP "_gmtime32_s.c",
- "secapi" OS_SEP "_gmtime64_s.c",
- "secapi" OS_SEP "_localtime32_s.c",
- "secapi" OS_SEP "_localtime64_s.c",
- "secapi" OS_SEP "_mktemp_s.c",
- "secapi" OS_SEP "_sopen_s.c",
- "secapi" OS_SEP "_strdate_s.c",
- "secapi" OS_SEP "_strtime_s.c",
- "secapi" OS_SEP "_umask_s.c",
- "secapi" OS_SEP "_vcprintf_s.c",
- "secapi" OS_SEP "_vcprintf_s_l.c",
- "secapi" OS_SEP "_vcwprintf_s.c",
- "secapi" OS_SEP "_vcwprintf_s_l.c",
- "secapi" OS_SEP "_vscprintf_p.c",
- "secapi" OS_SEP "_vscwprintf_p.c",
- "secapi" OS_SEP "_vswprintf_p.c",
- "secapi" OS_SEP "_waccess_s.c",
- "secapi" OS_SEP "_wasctime_s.c",
- "secapi" OS_SEP "_wctime32_s.c",
- "secapi" OS_SEP "_wctime64_s.c",
- "secapi" OS_SEP "_wstrtime_s.c",
- "secapi" OS_SEP "_wmktemp_s.c",
- "secapi" OS_SEP "_wstrdate_s.c",
- "secapi" OS_SEP "asctime_s.c",
- "secapi" OS_SEP "memcpy_s.c",
- "secapi" OS_SEP "memmove_s.c",
- "secapi" OS_SEP "rand_s.c",
- "secapi" OS_SEP "sprintf_s.c",
- "secapi" OS_SEP "strerror_s.c",
- "secapi" OS_SEP "vsprintf_s.c",
- "secapi" OS_SEP "wmemcpy_s.c",
- "secapi" OS_SEP "wmemmove_s.c",
- "stdio" OS_SEP "mingw_lock.c",
-};
-
-static const char *msvcrt_i386_src[] = {
- "misc" OS_SEP "lc_locale_func.c",
- "misc" OS_SEP "___mb_cur_max_func.c",
-};
-
-static const char *msvcrt_other_src[] = {
- "misc" OS_SEP "__p___argv.c",
- "misc" OS_SEP "__p__acmdln.c",
- "misc" OS_SEP "__p__fmode.c",
- "misc" OS_SEP "__p__wcmdln.c",
-};
-
-static const char *mingwex_generic_src[] = {
- "complex" OS_SEP "_cabs.c",
- "complex" OS_SEP "cabs.c",
- "complex" OS_SEP "cabsf.c",
- "complex" OS_SEP "cabsl.c",
- "complex" OS_SEP "cacos.c",
- "complex" OS_SEP "cacosf.c",
- "complex" OS_SEP "cacosl.c",
- "complex" OS_SEP "carg.c",
- "complex" OS_SEP "cargf.c",
- "complex" OS_SEP "cargl.c",
- "complex" OS_SEP "casin.c",
- "complex" OS_SEP "casinf.c",
- "complex" OS_SEP "casinl.c",
- "complex" OS_SEP "catan.c",
- "complex" OS_SEP "catanf.c",
- "complex" OS_SEP "catanl.c",
- "complex" OS_SEP "ccos.c",
- "complex" OS_SEP "ccosf.c",
- "complex" OS_SEP "ccosl.c",
- "complex" OS_SEP "cexp.c",
- "complex" OS_SEP "cexpf.c",
- "complex" OS_SEP "cexpl.c",
- "complex" OS_SEP "cimag.c",
- "complex" OS_SEP "cimagf.c",
- "complex" OS_SEP "cimagl.c",
- "complex" OS_SEP "clog.c",
- "complex" OS_SEP "clog10.c",
- "complex" OS_SEP "clog10f.c",
- "complex" OS_SEP "clog10l.c",
- "complex" OS_SEP "clogf.c",
- "complex" OS_SEP "clogl.c",
- "complex" OS_SEP "conj.c",
- "complex" OS_SEP "conjf.c",
- "complex" OS_SEP "conjl.c",
- "complex" OS_SEP "cpow.c",
- "complex" OS_SEP "cpowf.c",
- "complex" OS_SEP "cpowl.c",
- "complex" OS_SEP "cproj.c",
- "complex" OS_SEP "cprojf.c",
- "complex" OS_SEP "cprojl.c",
- "complex" OS_SEP "creal.c",
- "complex" OS_SEP "crealf.c",
- "complex" OS_SEP "creall.c",
- "complex" OS_SEP "csin.c",
- "complex" OS_SEP "csinf.c",
- "complex" OS_SEP "csinl.c",
- "complex" OS_SEP "csqrt.c",
- "complex" OS_SEP "csqrtf.c",
- "complex" OS_SEP "csqrtl.c",
- "complex" OS_SEP "ctan.c",
- "complex" OS_SEP "ctanf.c",
- "complex" OS_SEP "ctanl.c",
- "crt" OS_SEP "dllentry.c",
- "crt" OS_SEP "dllmain.c",
- "gdtoa" OS_SEP "arithchk.c",
- "gdtoa" OS_SEP "dmisc.c",
- "gdtoa" OS_SEP "dtoa.c",
- "gdtoa" OS_SEP "g__fmt.c",
- "gdtoa" OS_SEP "g_dfmt.c",
- "gdtoa" OS_SEP "g_ffmt.c",
- "gdtoa" OS_SEP "g_xfmt.c",
- "gdtoa" OS_SEP "gdtoa.c",
- "gdtoa" OS_SEP "gethex.c",
- "gdtoa" OS_SEP "gmisc.c",
- "gdtoa" OS_SEP "hd_init.c",
- "gdtoa" OS_SEP "hexnan.c",
- "gdtoa" OS_SEP "misc.c",
- "gdtoa" OS_SEP "qnan.c",
- "gdtoa" OS_SEP "smisc.c",
- "gdtoa" OS_SEP "strtodg.c",
- "gdtoa" OS_SEP "strtodnrp.c",
- "gdtoa" OS_SEP "strtof.c",
- "gdtoa" OS_SEP "strtopx.c",
- "gdtoa" OS_SEP "sum.c",
- "gdtoa" OS_SEP "ulp.c",
- "math" OS_SEP "abs64.c",
- "math" OS_SEP "cbrt.c",
- "math" OS_SEP "cbrtf.c",
- "math" OS_SEP "cbrtl.c",
- "math" OS_SEP "cephes_emath.c",
- "math" OS_SEP "copysign.c",
- "math" OS_SEP "copysignf.c",
- "math" OS_SEP "coshf.c",
- "math" OS_SEP "coshl.c",
- "math" OS_SEP "erfl.c",
- "math" OS_SEP "expf.c",
- "math" OS_SEP "fabs.c",
- "math" OS_SEP "fabsf.c",
- "math" OS_SEP "fabsl.c",
- "math" OS_SEP "fdim.c",
- "math" OS_SEP "fdimf.c",
- "math" OS_SEP "fdiml.c",
- "math" OS_SEP "fma.c",
- "math" OS_SEP "fmaf.c",
- "math" OS_SEP "fmal.c",
- "math" OS_SEP "fmax.c",
- "math" OS_SEP "fmaxf.c",
- "math" OS_SEP "fmaxl.c",
- "math" OS_SEP "fmin.c",
- "math" OS_SEP "fminf.c",
- "math" OS_SEP "fminl.c",
- "math" OS_SEP "fp_consts.c",
- "math" OS_SEP "fp_constsf.c",
- "math" OS_SEP "fp_constsl.c",
- "math" OS_SEP "fpclassify.c",
- "math" OS_SEP "fpclassifyf.c",
- "math" OS_SEP "fpclassifyl.c",
- "math" OS_SEP "frexpf.c",
- "math" OS_SEP "hypot.c",
- "math" OS_SEP "hypotf.c",
- "math" OS_SEP "hypotl.c",
- "math" OS_SEP "isnan.c",
- "math" OS_SEP "isnanf.c",
- "math" OS_SEP "isnanl.c",
- "math" OS_SEP "ldexpf.c",
- "math" OS_SEP "lgamma.c",
- "math" OS_SEP "lgammaf.c",
- "math" OS_SEP "lgammal.c",
- "math" OS_SEP "llrint.c",
- "math" OS_SEP "llrintf.c",
- "math" OS_SEP "llrintl.c",
- "math" OS_SEP "llround.c",
- "math" OS_SEP "llroundf.c",
- "math" OS_SEP "llroundl.c",
- "math" OS_SEP "log10f.c",
- "math" OS_SEP "logf.c",
- "math" OS_SEP "lrint.c",
- "math" OS_SEP "lrintf.c",
- "math" OS_SEP "lrintl.c",
- "math" OS_SEP "lround.c",
- "math" OS_SEP "lroundf.c",
- "math" OS_SEP "lroundl.c",
- "math" OS_SEP "modf.c",
- "math" OS_SEP "modff.c",
- "math" OS_SEP "modfl.c",
- "math" OS_SEP "nextafterf.c",
- "math" OS_SEP "nextafterl.c",
- "math" OS_SEP "nexttoward.c",
- "math" OS_SEP "nexttowardf.c",
- "math" OS_SEP "powf.c",
- "math" OS_SEP "powi.c",
- "math" OS_SEP "powif.c",
- "math" OS_SEP "powil.c",
- "math" OS_SEP "rint.c",
- "math" OS_SEP "rintf.c",
- "math" OS_SEP "rintl.c",
- "math" OS_SEP "round.c",
- "math" OS_SEP "roundf.c",
- "math" OS_SEP "roundl.c",
- "math" OS_SEP "s_erf.c",
- "math" OS_SEP "sf_erf.c",
- "math" OS_SEP "signbit.c",
- "math" OS_SEP "signbitf.c",
- "math" OS_SEP "signbitl.c",
- "math" OS_SEP "signgam.c",
- "math" OS_SEP "sinhf.c",
- "math" OS_SEP "sinhl.c",
- "math" OS_SEP "sqrt.c",
- "math" OS_SEP "sqrtf.c",
- "math" OS_SEP "sqrtl.c",
- "math" OS_SEP "tanhf.c",
- "math" OS_SEP "tanhl.c",
- "math" OS_SEP "tgamma.c",
- "math" OS_SEP "tgammaf.c",
- "math" OS_SEP "tgammal.c",
- "math" OS_SEP "truncl.c",
- "misc" OS_SEP "alarm.c",
- "misc" OS_SEP "basename.c",
- "misc" OS_SEP "btowc.c",
- "misc" OS_SEP "delay-f.c",
- "misc" OS_SEP "delay-n.c",
- "misc" OS_SEP "delayimp.c",
- "misc" OS_SEP "dirent.c",
- "misc" OS_SEP "dirname.c",
- "misc" OS_SEP "feclearexcept.c",
- "misc" OS_SEP "fegetenv.c",
- "misc" OS_SEP "fegetexceptflag.c",
- "misc" OS_SEP "fegetround.c",
- "misc" OS_SEP "feholdexcept.c",
- "misc" OS_SEP "feraiseexcept.c",
- "misc" OS_SEP "fesetenv.c",
- "misc" OS_SEP "fesetexceptflag.c",
- "misc" OS_SEP "fesetround.c",
- "misc" OS_SEP "fetestexcept.c",
- "misc" OS_SEP "feupdateenv.c",
- "misc" OS_SEP "ftruncate.c",
- "misc" OS_SEP "ftw.c",
- "misc" OS_SEP "ftw64.c",
- "misc" OS_SEP "fwide.c",
- "misc" OS_SEP "getlogin.c",
- "misc" OS_SEP "getopt.c",
- "misc" OS_SEP "gettimeofday.c",
- "misc" OS_SEP "imaxabs.c",
- "misc" OS_SEP "imaxdiv.c",
- "misc" OS_SEP "isblank.c",
- "misc" OS_SEP "iswblank.c",
- "misc" OS_SEP "mbrtowc.c",
- "misc" OS_SEP "mbsinit.c",
- "misc" OS_SEP "mempcpy.c",
- "misc" OS_SEP "mingw-aligned-malloc.c",
- "misc" OS_SEP "mingw-fseek.c",
- "misc" OS_SEP "mingw_getsp.S",
- "misc" OS_SEP "mingw_matherr.c",
- "misc" OS_SEP "mingw_mbwc_convert.c",
- "misc" OS_SEP "mingw_usleep.c",
- "misc" OS_SEP "mingw_wcstod.c",
- "misc" OS_SEP "mingw_wcstof.c",
- "misc" OS_SEP "mingw_wcstold.c",
- "misc" OS_SEP "mkstemp.c",
- "misc" OS_SEP "seterrno.c",
- "misc" OS_SEP "sleep.c",
- "misc" OS_SEP "strnlen.c",
- "misc" OS_SEP "strsafe.c",
- "misc" OS_SEP "strtoimax.c",
- "misc" OS_SEP "strtold.c",
- "misc" OS_SEP "strtoumax.c",
- "misc" OS_SEP "tdelete.c",
- "misc" OS_SEP "tfind.c",
- "misc" OS_SEP "tsearch.c",
- "misc" OS_SEP "twalk.c",
- "misc" OS_SEP "uchar_c16rtomb.c",
- "misc" OS_SEP "uchar_c32rtomb.c",
- "misc" OS_SEP "uchar_mbrtoc16.c",
- "misc" OS_SEP "uchar_mbrtoc32.c",
- "misc" OS_SEP "wassert.c",
- "misc" OS_SEP "wcrtomb.c",
- "misc" OS_SEP "wcsnlen.c",
- "misc" OS_SEP "wcstof.c",
- "misc" OS_SEP "wcstoimax.c",
- "misc" OS_SEP "wcstold.c",
- "misc" OS_SEP "wcstoumax.c",
- "misc" OS_SEP "wctob.c",
- "misc" OS_SEP "wctrans.c",
- "misc" OS_SEP "wctype.c",
- "misc" OS_SEP "wdirent.c",
- "misc" OS_SEP "winbs_uint64.c",
- "misc" OS_SEP "winbs_ulong.c",
- "misc" OS_SEP "winbs_ushort.c",
- "misc" OS_SEP "wmemchr.c",
- "misc" OS_SEP "wmemcmp.c",
- "misc" OS_SEP "wmemcpy.c",
- "misc" OS_SEP "wmemmove.c",
- "misc" OS_SEP "wmempcpy.c",
- "misc" OS_SEP "wmemset.c",
- "stdio" OS_SEP "_Exit.c",
- "stdio" OS_SEP "_findfirst64i32.c",
- "stdio" OS_SEP "_findnext64i32.c",
- "stdio" OS_SEP "_fstat.c",
- "stdio" OS_SEP "_fstat64i32.c",
- "stdio" OS_SEP "_ftime.c",
- "stdio" OS_SEP "_getc_nolock.c",
- "stdio" OS_SEP "_getwc_nolock.c",
- "stdio" OS_SEP "_putc_nolock.c",
- "stdio" OS_SEP "_putwc_nolock.c",
- "stdio" OS_SEP "_stat.c",
- "stdio" OS_SEP "_stat64i32.c",
- "stdio" OS_SEP "_wfindfirst64i32.c",
- "stdio" OS_SEP "_wfindnext64i32.c",
- "stdio" OS_SEP "_wstat.c",
- "stdio" OS_SEP "_wstat64i32.c",
- "stdio" OS_SEP "asprintf.c",
- "stdio" OS_SEP "atoll.c",
- "stdio" OS_SEP "fgetpos64.c",
- "stdio" OS_SEP "fopen64.c",
- "stdio" OS_SEP "fseeko32.c",
- "stdio" OS_SEP "fseeko64.c",
- "stdio" OS_SEP "fsetpos64.c",
- "stdio" OS_SEP "ftello.c",
- "stdio" OS_SEP "ftello64.c",
- "stdio" OS_SEP "ftruncate64.c",
- "stdio" OS_SEP "lltoa.c",
- "stdio" OS_SEP "lltow.c",
- "stdio" OS_SEP "lseek64.c",
- "stdio" OS_SEP "mingw_asprintf.c",
- "stdio" OS_SEP "mingw_fprintf.c",
- "stdio" OS_SEP "mingw_fprintfw.c",
- "stdio" OS_SEP "mingw_fscanf.c",
- "stdio" OS_SEP "mingw_fwscanf.c",
- "stdio" OS_SEP "mingw_pformat.c",
- "stdio" OS_SEP "mingw_pformatw.c",
- "stdio" OS_SEP "mingw_printf.c",
- "stdio" OS_SEP "mingw_printfw.c",
- "stdio" OS_SEP "mingw_scanf.c",
- "stdio" OS_SEP "mingw_snprintf.c",
- "stdio" OS_SEP "mingw_snprintfw.c",
- "stdio" OS_SEP "mingw_sprintf.c",
- "stdio" OS_SEP "mingw_sprintfw.c",
- "stdio" OS_SEP "mingw_sscanf.c",
- "stdio" OS_SEP "mingw_swscanf.c",
- "stdio" OS_SEP "mingw_vasprintf.c",
- "stdio" OS_SEP "mingw_vfprintf.c",
- "stdio" OS_SEP "mingw_vfprintfw.c",
- "stdio" OS_SEP "mingw_vfscanf.c",
- "stdio" OS_SEP "mingw_vprintf.c",
- "stdio" OS_SEP "mingw_vprintfw.c",
- "stdio" OS_SEP "mingw_vsnprintf.c",
- "stdio" OS_SEP "mingw_vsnprintfw.c",
- "stdio" OS_SEP "mingw_vsprintf.c",
- "stdio" OS_SEP "mingw_vsprintfw.c",
- "stdio" OS_SEP "mingw_wscanf.c",
- "stdio" OS_SEP "mingw_wvfscanf.c",
- "stdio" OS_SEP "scanf.S",
- "stdio" OS_SEP "snprintf.c",
- "stdio" OS_SEP "snwprintf.c",
- "stdio" OS_SEP "strtof.c",
- "stdio" OS_SEP "strtok_r.c",
- "stdio" OS_SEP "truncate.c",
- "stdio" OS_SEP "ulltoa.c",
- "stdio" OS_SEP "ulltow.c",
- "stdio" OS_SEP "vasprintf.c",
- "stdio" OS_SEP "vfscanf.c",
- "stdio" OS_SEP "vfscanf2.S",
- "stdio" OS_SEP "vfwscanf.c",
- "stdio" OS_SEP "vfwscanf2.S",
- "stdio" OS_SEP "vscanf.c",
- "stdio" OS_SEP "vscanf2.S",
- "stdio" OS_SEP "vsnprintf.c",
- "stdio" OS_SEP "vsnwprintf.c",
- "stdio" OS_SEP "vsscanf.c",
- "stdio" OS_SEP "vsscanf2.S",
- "stdio" OS_SEP "vswscanf.c",
- "stdio" OS_SEP "vswscanf2.S",
- "stdio" OS_SEP "vwscanf.c",
- "stdio" OS_SEP "vwscanf2.S",
- "stdio" OS_SEP "wtoll.c",
-};
-
-static const char *mingwex_x86_src[] = {
- "math" OS_SEP "x86" OS_SEP "acosf.c",
- "math" OS_SEP "x86" OS_SEP "acosh.c",
- "math" OS_SEP "x86" OS_SEP "acoshf.c",
- "math" OS_SEP "x86" OS_SEP "acoshl.c",
- "math" OS_SEP "x86" OS_SEP "acosl.c",
- "math" OS_SEP "x86" OS_SEP "asinf.c",
- "math" OS_SEP "x86" OS_SEP "asinh.c",
- "math" OS_SEP "x86" OS_SEP "asinhf.c",
- "math" OS_SEP "x86" OS_SEP "asinhl.c",
- "math" OS_SEP "x86" OS_SEP "asinl.c",
- "math" OS_SEP "x86" OS_SEP "atan2.c",
- "math" OS_SEP "x86" OS_SEP "atan2f.c",
- "math" OS_SEP "x86" OS_SEP "atan2l.c",
- "math" OS_SEP "x86" OS_SEP "atanf.c",
- "math" OS_SEP "x86" OS_SEP "atanh.c",
- "math" OS_SEP "x86" OS_SEP "atanhf.c",
- "math" OS_SEP "x86" OS_SEP "atanhl.c",
- "math" OS_SEP "x86" OS_SEP "atanl.c",
- "math" OS_SEP "x86" OS_SEP "ceilf.S",
- "math" OS_SEP "x86" OS_SEP "ceill.S",
- "math" OS_SEP "x86" OS_SEP "ceil.S",
- "math" OS_SEP "x86" OS_SEP "_chgsignl.S",
- "math" OS_SEP "x86" OS_SEP "copysignl.S",
- "math" OS_SEP "x86" OS_SEP "cos.c",
- "math" OS_SEP "x86" OS_SEP "cosf.c",
- "math" OS_SEP "x86" OS_SEP "cosl.c",
- "math" OS_SEP "x86" OS_SEP "cosl_internal.S",
- "math" OS_SEP "x86" OS_SEP "cossin.c",
- "math" OS_SEP "x86" OS_SEP "exp2f.S",
- "math" OS_SEP "x86" OS_SEP "exp2l.S",
- "math" OS_SEP "x86" OS_SEP "exp2.S",
- "math" OS_SEP "x86" OS_SEP "exp.c",
- "math" OS_SEP "x86" OS_SEP "expl.c",
- "math" OS_SEP "x86" OS_SEP "expm1.c",
- "math" OS_SEP "x86" OS_SEP "expm1f.c",
- "math" OS_SEP "x86" OS_SEP "expm1l.c",
- "math" OS_SEP "x86" OS_SEP "floorf.S",
- "math" OS_SEP "x86" OS_SEP "floorl.S",
- "math" OS_SEP "x86" OS_SEP "floor.S",
- "math" OS_SEP "x86" OS_SEP "fmod.c",
- "math" OS_SEP "x86" OS_SEP "fmodf.c",
- "math" OS_SEP "x86" OS_SEP "fmodl.c",
- "math" OS_SEP "x86" OS_SEP "fucom.c",
- "math" OS_SEP "x86" OS_SEP "ilogbf.S",
- "math" OS_SEP "x86" OS_SEP "ilogbl.S",
- "math" OS_SEP "x86" OS_SEP "ilogb.S",
- "math" OS_SEP "x86" OS_SEP "internal_logl.S",
- "math" OS_SEP "x86" OS_SEP "ldexp.c",
- "math" OS_SEP "x86" OS_SEP "ldexpl.c",
- "math" OS_SEP "x86" OS_SEP "log10l.S",
- "math" OS_SEP "x86" OS_SEP "log1pf.S",
- "math" OS_SEP "x86" OS_SEP "log1pl.S",
- "math" OS_SEP "x86" OS_SEP "log1p.S",
- "math" OS_SEP "x86" OS_SEP "log2f.S",
- "math" OS_SEP "x86" OS_SEP "log2l.S",
- "math" OS_SEP "x86" OS_SEP "log2.S",
- "math" OS_SEP "x86" OS_SEP "logb.c",
- "math" OS_SEP "x86" OS_SEP "logbf.c",
- "math" OS_SEP "x86" OS_SEP "logbl.c",
- "math" OS_SEP "x86" OS_SEP "log.c",
- "math" OS_SEP "x86" OS_SEP "logl.c",
- "math" OS_SEP "x86" OS_SEP "nearbyintf.S",
- "math" OS_SEP "x86" OS_SEP "nearbyintl.S",
- "math" OS_SEP "x86" OS_SEP "nearbyint.S",
- "math" OS_SEP "x86" OS_SEP "pow.c",
- "math" OS_SEP "x86" OS_SEP "powl.c",
- "math" OS_SEP "x86" OS_SEP "remainderf.S",
- "math" OS_SEP "x86" OS_SEP "remainderl.S",
- "math" OS_SEP "x86" OS_SEP "remainder.S",
- "math" OS_SEP "x86" OS_SEP "remquof.S",
- "math" OS_SEP "x86" OS_SEP "remquol.S",
- "math" OS_SEP "x86" OS_SEP "remquo.S",
- "math" OS_SEP "x86" OS_SEP "scalbnf.S",
- "math" OS_SEP "x86" OS_SEP "scalbnl.S",
- "math" OS_SEP "x86" OS_SEP "scalbn.S",
- "math" OS_SEP "x86" OS_SEP "sin.c",
- "math" OS_SEP "x86" OS_SEP "sinf.c",
- "math" OS_SEP "x86" OS_SEP "sinl.c",
- "math" OS_SEP "x86" OS_SEP "sinl_internal.S",
- "math" OS_SEP "x86" OS_SEP "tanf.c",
- "math" OS_SEP "x86" OS_SEP "tanl.S",
- "math" OS_SEP "x86" OS_SEP "truncf.S",
- "math" OS_SEP "x86" OS_SEP "trunc.S",
-};
-
-static const char *mingwex_arm32_src[] = {
- "math" OS_SEP "arm" OS_SEP "_chgsignl.S",
- "math" OS_SEP "arm" OS_SEP "exp2.c",
- "math" OS_SEP "arm" OS_SEP "nearbyint.S",
- "math" OS_SEP "arm" OS_SEP "nearbyintf.S",
- "math" OS_SEP "arm" OS_SEP "nearbyintl.S",
- "math" OS_SEP "arm" OS_SEP "trunc.S",
- "math" OS_SEP "arm" OS_SEP "truncf.S",
-};
-
-static const char *mingwex_arm64_src[] = {
- "math" OS_SEP "arm64" OS_SEP "_chgsignl.S",
- "math" OS_SEP "arm64" OS_SEP "exp2f.S",
- "math" OS_SEP "arm64" OS_SEP "exp2.S",
- "math" OS_SEP "arm64" OS_SEP "nearbyintf.S",
- "math" OS_SEP "arm64" OS_SEP "nearbyintl.S",
- "math" OS_SEP "arm64" OS_SEP "nearbyint.S",
- "math" OS_SEP "arm64" OS_SEP "truncf.S",
- "math" OS_SEP "arm64" OS_SEP "trunc.S",
-};
-
-static const char *mingw_uuid_src[] = {
- "libsrc/ativscp-uuid.c",
- "libsrc/atsmedia-uuid.c",
- "libsrc/bth-uuid.c",
- "libsrc/cguid-uuid.c",
- "libsrc/comcat-uuid.c",
- "libsrc/devguid.c",
- "libsrc/docobj-uuid.c",
- "libsrc/dxva-uuid.c",
- "libsrc/exdisp-uuid.c",
- "libsrc/extras-uuid.c",
- "libsrc/fwp-uuid.c",
- "libsrc/guid_nul.c",
- "libsrc/hlguids-uuid.c",
- "libsrc/hlink-uuid.c",
- "libsrc/mlang-uuid.c",
- "libsrc/msctf-uuid.c",
- "libsrc/mshtmhst-uuid.c",
- "libsrc/mshtml-uuid.c",
- "libsrc/msxml-uuid.c",
- "libsrc/netcon-uuid.c",
- "libsrc/ntddkbd-uuid.c",
- "libsrc/ntddmou-uuid.c",
- "libsrc/ntddpar-uuid.c",
- "libsrc/ntddscsi-uuid.c",
- "libsrc/ntddser-uuid.c",
- "libsrc/ntddstor-uuid.c",
- "libsrc/ntddvdeo-uuid.c",
- "libsrc/oaidl-uuid.c",
- "libsrc/objidl-uuid.c",
- "libsrc/objsafe-uuid.c",
- "libsrc/ocidl-uuid.c",
- "libsrc/oleacc-uuid.c",
- "libsrc/olectlid-uuid.c",
- "libsrc/oleidl-uuid.c",
- "libsrc/power-uuid.c",
- "libsrc/powrprof-uuid.c",
- "libsrc/uianimation-uuid.c",
- "libsrc/usbcamdi-uuid.c",
- "libsrc/usbiodef-uuid.c",
- "libsrc/uuid.c",
- "libsrc/vds-uuid.c",
- "libsrc/virtdisk-uuid.c",
- "libsrc/wia-uuid.c",
-};
-
-struct MinGWDef {
- const char *name;
- bool always_link;
-};
-static const MinGWDef mingw_def_list[] = {
- {"advapi32",true},
- {"bcrypt", false},
- {"comctl32",false},
- {"comdlg32",false},
- {"crypt32", false},
- {"cryptnet",false},
- {"gdi32", false},
- {"imm32", false},
- {"kernel32",true},
- {"lz32", false},
- {"mpr", false},
- {"msvcrt", true},
- {"mswsock", false},
- {"ncrypt", false},
- {"netapi32",false},
- {"ntdll", true},
- {"ole32", false},
- {"oleaut32",false},
- {"opengl32",false},
- {"psapi", false},
- {"rpcns4", false},
- {"rpcrt4", false},
- {"scarddlg",false},
- {"setupapi",false},
- {"shell32", true},
- {"shlwapi", false},
- {"urlmon", false},
- {"user32", true},
- {"version", false},
- {"winmm", false},
- {"winscard",false},
- {"winspool",false},
- {"wintrust",false},
- {"ws2_32", false},
-};
-
-struct LinkJob {
- CodeGen *codegen;
- ZigList args;
- bool link_in_crt;
- HashMap rpath_table;
- Stage2ProgressNode *build_dep_prog_node;
-};
-
-static const char *build_libc_object(CodeGen *parent_gen, const char *name, CFile *c_file,
- Stage2ProgressNode *progress_node)
-{
- CodeGen *child_gen = create_child_codegen(parent_gen, nullptr, OutTypeObj, nullptr, name, progress_node);
- child_gen->root_out_name = buf_create_from_str(name);
- ZigList c_source_files = {0};
- c_source_files.append(c_file);
- child_gen->c_source_files = c_source_files;
- codegen_build_and_link(child_gen);
- return buf_ptr(&child_gen->bin_file_output_path);
-}
-
-static const char *path_from_zig_lib(CodeGen *g, const char *dir, const char *subpath) {
- Buf *dir1 = buf_alloc();
- os_path_join(g->zig_lib_dir, buf_create_from_str(dir), dir1);
- Buf *result = buf_alloc();
- os_path_join(dir1, buf_create_from_str(subpath), result);
- return buf_ptr(result);
-}
-
-static const char *path_from_libc(CodeGen *g, const char *subpath) {
- return path_from_zig_lib(g, "libc", subpath);
-}
-
-static const char *path_from_libunwind(CodeGen *g, const char *subpath) {
- return path_from_zig_lib(g, "libunwind", subpath);
-}
-
-static const char *build_libunwind(CodeGen *parent, Stage2ProgressNode *progress_node) {
- CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "unwind", progress_node);
- LinkLib *new_link_lib = codegen_add_link_lib(child_gen, buf_create_from_str("c"));
- new_link_lib->provided_explicitly = false;
- enum SrcKind {
- SrcCpp,
- SrcC,
- SrcAsm,
- };
- static const struct {
- const char *path;
- SrcKind kind;
- } unwind_src[] = {
- {"src" OS_SEP "libunwind.cpp", SrcCpp},
- {"src" OS_SEP "Unwind-EHABI.cpp", SrcCpp},
- {"src" OS_SEP "Unwind-seh.cpp", SrcCpp},
-
- {"src" OS_SEP "UnwindLevel1.c", SrcC},
- {"src" OS_SEP "UnwindLevel1-gcc-ext.c", SrcC},
- {"src" OS_SEP "Unwind-sjlj.c", SrcC},
-
- {"src" OS_SEP "UnwindRegistersRestore.S", SrcAsm},
- {"src" OS_SEP "UnwindRegistersSave.S", SrcAsm},
- };
- ZigList c_source_files = {0};
- for (size_t i = 0; i < array_length(unwind_src); i += 1) {
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = path_from_libunwind(parent, unwind_src[i].path);
- switch (unwind_src[i].kind) {
- case SrcC:
- c_file->args.append("-std=c99");
- break;
- case SrcCpp:
- c_file->args.append("-fno-rtti");
- c_file->args.append("-I");
- c_file->args.append(path_from_zig_lib(parent, "libcxx", "include"));
- break;
- case SrcAsm:
- break;
- }
- c_file->args.append("-I");
- c_file->args.append(path_from_libunwind(parent, "include"));
- if (target_supports_fpic(parent->zig_target)) {
- c_file->args.append("-fPIC");
- }
- c_file->args.append("-D_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS");
- c_file->args.append("-Wa,--noexecstack");
-
- // This is intentionally always defined because the macro definition means, should it only
- // build for the target specified by compiler defines. Since we pass -target the compiler
- // defines will be correct.
- c_file->args.append("-D_LIBUNWIND_IS_NATIVE_ONLY");
-
- if (parent->build_mode == BuildModeDebug) {
- c_file->args.append("-D_DEBUG");
- }
- if (parent->is_single_threaded) {
- c_file->args.append("-D_LIBUNWIND_HAS_NO_THREADS");
- }
- c_file->args.append("-Wno-bitwise-conditional-parentheses");
- c_source_files.append(c_file);
- }
- child_gen->c_source_files = c_source_files;
- codegen_build_and_link(child_gen);
- return buf_ptr(&child_gen->bin_file_output_path);
-}
-
-static void mingw_add_cc_args(CodeGen *parent, CFile *c_file) {
- c_file->args.append("-DHAVE_CONFIG_H");
-
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "include",
- buf_ptr(parent->zig_lib_dir))));
-
- c_file->args.append("-isystem");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "any-windows-any",
- buf_ptr(parent->zig_lib_dir))));
-
- if (target_is_arm(parent->zig_target) &&
- target_arch_pointer_bit_width(parent->zig_target->arch) == 32)
- {
- c_file->args.append("-mfpu=vfp");
- }
-
- c_file->args.append("-std=gnu11");
- c_file->args.append("-D_CRTBLD");
- c_file->args.append("-D_WIN32_WINNT=0x0f00");
- c_file->args.append("-D__MSVCRT_VERSION__=0x700");
-}
-
-static void glibc_add_include_dirs_arch(CFile *c_file, ZigLLVM_ArchType arch, const char *nptl, const char *dir) {
- bool is_x86 = arch == ZigLLVM_x86 || arch == ZigLLVM_x86_64;
- bool is_aarch64 = arch == ZigLLVM_aarch64 || arch == ZigLLVM_aarch64_be;
- bool is_mips = arch == ZigLLVM_mips || arch == ZigLLVM_mipsel ||
- arch == ZigLLVM_mips64el || arch == ZigLLVM_mips64;
- bool is_arm = arch == ZigLLVM_arm || arch == ZigLLVM_armeb;
- bool is_ppc = arch == ZigLLVM_ppc || arch == ZigLLVM_ppc64 || arch == ZigLLVM_ppc64le;
- bool is_riscv = arch == ZigLLVM_riscv32 || arch == ZigLLVM_riscv64;
- bool is_sparc = arch == ZigLLVM_sparc || arch == ZigLLVM_sparcel || arch == ZigLLVM_sparcv9;
- bool is_64 = target_arch_pointer_bit_width(arch) == 64;
-
- if (is_x86) {
- if (arch == ZigLLVM_x86_64) {
- if (nptl != nullptr) {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "x86_64" OS_SEP "%s", dir, nptl)));
- } else {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "x86_64", dir)));
- }
- } else if (arch == ZigLLVM_x86) {
- if (nptl != nullptr) {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "i386" OS_SEP "%s", dir, nptl)));
- } else {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "i386", dir)));
- }
- }
- if (nptl != nullptr) {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "x86" OS_SEP "%s", dir, nptl)));
- } else {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "x86", dir)));
- }
- } else if (is_arm) {
- if (nptl != nullptr) {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "arm" OS_SEP "%s", dir, nptl)));
- } else {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "arm", dir)));
- }
- } else if (is_mips) {
- if (nptl != nullptr) {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "mips" OS_SEP "%s", dir, nptl)));
- } else {
- if (is_64) {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "mips" OS_SEP "mips64", dir)));
- } else {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "mips" OS_SEP "mips32", dir)));
- }
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "mips", dir)));
- }
- } else if (is_sparc) {
- if (nptl != nullptr) {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "sparc" OS_SEP "%s", dir, nptl)));
- } else {
- if (is_64) {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "sparc" OS_SEP "sparc64", dir)));
- } else {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "sparc" OS_SEP "sparc32", dir)));
- }
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "sparc", dir)));
- }
- } else if (is_aarch64) {
- if (nptl != nullptr) {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "aarch64" OS_SEP "%s", dir, nptl)));
- } else {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "aarch64", dir)));
- }
- } else if (is_ppc) {
- if (nptl != nullptr) {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "powerpc" OS_SEP "%s", dir, nptl)));
- } else {
- if (is_64) {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "powerpc" OS_SEP "powerpc64", dir)));
- } else {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "powerpc" OS_SEP "powerpc32", dir)));
- }
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "powerpc", dir)));
- }
- } else if (is_riscv) {
- if (nptl != nullptr) {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "riscv" OS_SEP "%s", dir, nptl)));
- } else {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "riscv", dir)));
- }
- }
-}
-
-static void glibc_add_include_dirs(CodeGen *parent, CFile *c_file) {
- ZigLLVM_ArchType arch = parent->zig_target->arch;
- const char *nptl = (parent->zig_target->os == OsLinux) ? "nptl" : "htl";
- const char *glibc = path_from_libc(parent, "glibc");
-
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "include", glibc)));
-
- if (parent->zig_target->os == OsLinux) {
- glibc_add_include_dirs_arch(c_file, arch, nullptr,
- path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP "unix" OS_SEP "sysv" OS_SEP "linux"));
- }
-
- if (nptl != nullptr) {
- glibc_add_include_dirs_arch(c_file, arch, nptl, path_from_libc(parent, "glibc" OS_SEP "sysdeps"));
- }
-
- if (parent->zig_target->os == OsLinux) {
- c_file->args.append("-I");
- c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP
- "unix" OS_SEP "sysv" OS_SEP "linux" OS_SEP "generic"));
-
- c_file->args.append("-I");
- c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP
- "unix" OS_SEP "sysv" OS_SEP "linux" OS_SEP "include"));
- c_file->args.append("-I");
- c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP
- "unix" OS_SEP "sysv" OS_SEP "linux"));
- }
- if (nptl != nullptr) {
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "sysdeps" OS_SEP "%s", glibc, nptl)));
- }
-
- c_file->args.append("-I");
- c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP "pthread"));
-
- c_file->args.append("-I");
- c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP "unix" OS_SEP "sysv"));
-
- glibc_add_include_dirs_arch(c_file, arch, nullptr,
- path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP "unix"));
-
- c_file->args.append("-I");
- c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP "unix"));
-
- glibc_add_include_dirs_arch(c_file, arch, nullptr, path_from_libc(parent, "glibc" OS_SEP "sysdeps"));
-
- c_file->args.append("-I");
- c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP "generic"));
-
- c_file->args.append("-I");
- c_file->args.append(path_from_libc(parent, "glibc"));
-
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "%s-%s-%s",
- buf_ptr(parent->zig_lib_dir), target_arch_name(parent->zig_target->arch),
- target_os_name(parent->zig_target->os), target_abi_name(parent->zig_target->abi))));
-
- c_file->args.append("-I");
- c_file->args.append(path_from_libc(parent, "include" OS_SEP "generic-glibc"));
-
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "%s-linux-any",
- buf_ptr(parent->zig_lib_dir), target_arch_name(parent->zig_target->arch))));
-
- c_file->args.append("-I");
- c_file->args.append(path_from_libc(parent, "include" OS_SEP "any-linux-any"));
-}
-
-static const char *glibc_start_asm_path(CodeGen *parent, const char *file) {
- ZigLLVM_ArchType arch = parent->zig_target->arch;
- bool is_aarch64 = arch == ZigLLVM_aarch64 || arch == ZigLLVM_aarch64_be;
- bool is_mips = arch == ZigLLVM_mips || arch == ZigLLVM_mipsel ||
- arch == ZigLLVM_mips64el || arch == ZigLLVM_mips64;
- bool is_arm = arch == ZigLLVM_arm || arch == ZigLLVM_armeb;
- bool is_ppc = arch == ZigLLVM_ppc || arch == ZigLLVM_ppc64 || arch == ZigLLVM_ppc64le;
- bool is_riscv = arch == ZigLLVM_riscv32 || arch == ZigLLVM_riscv64;
- bool is_sparc = arch == ZigLLVM_sparc || arch == ZigLLVM_sparcel || arch == ZigLLVM_sparcv9;
- bool is_64 = target_arch_pointer_bit_width(arch) == 64;
-
- Buf result = BUF_INIT;
- buf_resize(&result, 0);
- buf_append_buf(&result, parent->zig_lib_dir);
- buf_append_str(&result, OS_SEP "libc" OS_SEP "glibc" OS_SEP "sysdeps" OS_SEP);
- if (is_sparc) {
- if (is_64) {
- buf_append_str(&result, "sparc" OS_SEP "sparc64");
- } else {
- buf_append_str(&result, "sparc" OS_SEP "sparc32");
- }
- } else if (is_arm) {
- buf_append_str(&result, "arm");
- } else if (is_mips) {
- buf_append_str(&result, "mips");
- } else if (arch == ZigLLVM_x86_64) {
- buf_append_str(&result, "x86_64");
- } else if (arch == ZigLLVM_x86) {
- buf_append_str(&result, "i386");
- } else if (is_aarch64) {
- buf_append_str(&result, "aarch64");
- } else if (is_riscv) {
- buf_append_str(&result, "riscv");
- } else if (is_ppc) {
- if (is_64) {
- buf_append_str(&result, "powerpc" OS_SEP "powerpc64");
- } else {
- buf_append_str(&result, "powerpc" OS_SEP "powerpc32");
- }
- }
-
- buf_append_str(&result, OS_SEP);
- buf_append_str(&result, file);
- return buf_ptr(&result);
-}
-
-static const char *musl_start_asm_path(CodeGen *parent, const char *file) {
- Buf *result = buf_sprintf("%s" OS_SEP "libc" OS_SEP "musl" OS_SEP "crt" OS_SEP "%s" OS_SEP "%s",
- buf_ptr(parent->zig_lib_dir), target_arch_musl_name(parent->zig_target->arch), file);
- return buf_ptr(result);
-}
-
-static void musl_add_cc_args(CodeGen *parent, CFile *c_file, bool want_O3) {
- c_file->args.append("-std=c99");
- c_file->args.append("-ffreestanding");
- // Musl adds these args to builds with gcc but clang does not support them.
- //c_file->args.append("-fexcess-precision=standard");
- //c_file->args.append("-frounding-math");
- c_file->args.append("-Wa,--noexecstack");
- c_file->args.append("-D_XOPEN_SOURCE=700");
-
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "musl" OS_SEP "arch" OS_SEP "%s",
- buf_ptr(parent->zig_lib_dir), target_arch_musl_name(parent->zig_target->arch))));
-
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "musl" OS_SEP "arch" OS_SEP "generic",
- buf_ptr(parent->zig_lib_dir))));
-
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "musl" OS_SEP "src" OS_SEP "include",
- buf_ptr(parent->zig_lib_dir))));
-
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "musl" OS_SEP "src" OS_SEP "internal",
- buf_ptr(parent->zig_lib_dir))));
-
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "musl" OS_SEP "include",
- buf_ptr(parent->zig_lib_dir))));
-
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf(
- "%s" OS_SEP "libc" OS_SEP "include" OS_SEP "%s-%s-musl",
- buf_ptr(parent->zig_lib_dir),
- target_arch_musl_name(parent->zig_target->arch),
- target_os_name(parent->zig_target->os))));
-
- c_file->args.append("-I");
- c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "generic-musl",
- buf_ptr(parent->zig_lib_dir))));
-
- if (want_O3)
- c_file->args.append("-O3");
- else
- c_file->args.append("-Os");
-
- c_file->args.append("-fomit-frame-pointer");
- c_file->args.append("-fno-unwind-tables");
- c_file->args.append("-fno-asynchronous-unwind-tables");
- c_file->args.append("-ffunction-sections");
- c_file->args.append("-fdata-sections");
-}
-
-static const char *musl_arch_names[] = {
- "aarch64",
- "arm",
- "generic",
- "i386",
- "m68k",
- "microblaze",
- "mips",
- "mips64",
- "mipsn32",
- "or1k",
- "powerpc",
- "powerpc64",
- "riscv64",
- "s390x",
- "sh",
- "x32",
- "x86_64",
-};
-
-static bool is_musl_arch_name(const char *name) {
- for (size_t i = 0; i < array_length(musl_arch_names); i += 1) {
- if (strcmp(name, musl_arch_names[i]) == 0)
- return true;
- }
- return false;
-}
-
-enum MuslSrc {
- MuslSrcAsm,
- MuslSrcNormal,
- MuslSrcO3,
-};
-
-static void add_musl_src_file(HashMap &source_table,
- const char *file_path)
-{
- Buf *src_file = buf_create_from_str(file_path);
-
- MuslSrc src_kind;
- if (buf_ends_with_str(src_file, ".c")) {
- bool want_O3 = buf_starts_with_str(src_file, "musl/src/malloc/") ||
- buf_starts_with_str(src_file, "musl/src/string/") ||
- buf_starts_with_str(src_file, "musl/src/internal/");
- src_kind = want_O3 ? MuslSrcO3 : MuslSrcNormal;
- } else if (buf_ends_with_str(src_file, ".s") || buf_ends_with_str(src_file, ".S")) {
- src_kind = MuslSrcAsm;
- } else {
- zig_unreachable();
- }
- if (ZIG_OS_SEP_CHAR != '/') {
- buf_replace(src_file, '/', ZIG_OS_SEP_CHAR);
- }
- source_table.put_unique(src_file, src_kind);
-}
-
-static const char *build_musl(CodeGen *parent, Stage2ProgressNode *progress_node) {
- CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "c", progress_node);
-
- // When there is a src//foo.* then it should substitute for src/foo.*
- // Even a .s file can substitute for a .c file.
-
- const char *target_musl_arch_name = target_arch_musl_name(parent->zig_target->arch);
-
- HashMap source_table = {};
- source_table.init(2000);
-
- for (size_t i = 0; i < array_length(ZIG_MUSL_SRC_FILES); i += 1) {
- add_musl_src_file(source_table, ZIG_MUSL_SRC_FILES[i]);
- }
-
- static const char *time32_compat_arch_list[] = {"arm", "i386", "mips", "powerpc"};
- for (size_t arch_i = 0; arch_i < array_length(time32_compat_arch_list); arch_i += 1) {
- if (strcmp(target_musl_arch_name, time32_compat_arch_list[arch_i]) == 0) {
- for (size_t i = 0; i < array_length(ZIG_MUSL_COMPAT_TIME32_FILES); i += 1) {
- add_musl_src_file(source_table, ZIG_MUSL_COMPAT_TIME32_FILES[i]);
- }
- }
- }
-
-
- ZigList c_source_files = {0};
-
- Buf dirname = BUF_INIT;
- Buf basename = BUF_INIT;
- Buf noextbasename = BUF_INIT;
- Buf dirbasename = BUF_INIT;
- Buf before_arch_dir = BUF_INIT;
-
- auto source_it = source_table.entry_iterator();
- for (;;) {
- auto *entry = source_it.next();
- if (!entry) break;
-
- Buf *src_file = entry->key;
- MuslSrc src_kind = entry->value;
-
- os_path_split(src_file, &dirname, &basename);
- os_path_extname(&basename, &noextbasename, nullptr);
- os_path_split(&dirname, &before_arch_dir, &dirbasename);
-
- bool is_arch_specific = false;
- // Architecture-specific implementations are under a / folder.
- if (is_musl_arch_name(buf_ptr(&dirbasename))) {
- // Not the architecture we're compiling for.
- if (strcmp(buf_ptr(&dirbasename), target_musl_arch_name) != 0)
- continue;
- is_arch_specific = true;
- }
-
- if (!is_arch_specific) {
- Buf override_path = BUF_INIT;
-
- // Look for an arch specific override.
- buf_resize(&override_path, 0);
- buf_appendf(&override_path, "%s" OS_SEP "%s" OS_SEP "%s.s",
- buf_ptr(&dirname), target_musl_arch_name, buf_ptr(&noextbasename));
- if (source_table.maybe_get(&override_path) != nullptr)
- continue;
-
- buf_resize(&override_path, 0);
- buf_appendf(&override_path, "%s" OS_SEP "%s" OS_SEP "%s.S",
- buf_ptr(&dirname), target_musl_arch_name, buf_ptr(&noextbasename));
- if (source_table.maybe_get(&override_path) != nullptr)
- continue;
-
- buf_resize(&override_path, 0);
- buf_appendf(&override_path, "%s" OS_SEP "%s" OS_SEP "%s.c",
- buf_ptr(&dirname), target_musl_arch_name, buf_ptr(&noextbasename));
- if (source_table.maybe_get(&override_path) != nullptr)
- continue;
- }
-
- Buf *full_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "%s",
- buf_ptr(parent->zig_lib_dir), buf_ptr(src_file));
-
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = buf_ptr(full_path);
-
- musl_add_cc_args(parent, c_file, src_kind == MuslSrcO3);
- c_file->args.append("-Qunused-arguments");
- c_file->args.append("-w"); // disable all warnings
-
- c_source_files.append(c_file);
- }
-
- child_gen->c_source_files = c_source_files;
- codegen_build_and_link(child_gen);
- return buf_ptr(&child_gen->bin_file_output_path);
-}
-
-static const char *build_libcxxabi(CodeGen *parent, Stage2ProgressNode *progress_node) {
- CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "c++abi", progress_node);
- codegen_add_link_lib(child_gen, buf_create_from_str("c"));
-
- ZigList c_source_files = {0};
-
- const char *cxxabi_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxxabi" OS_SEP "include",
- buf_ptr(parent->zig_lib_dir)));
- const char *cxx_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "include",
- buf_ptr(parent->zig_lib_dir)));
-
- for (size_t i = 0; i < array_length(ZIG_LIBCXXABI_FILES); i += 1) {
- const char *rel_src_path = ZIG_LIBCXXABI_FILES[i];
-
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxxabi" OS_SEP "%s",
- buf_ptr(parent->zig_lib_dir), rel_src_path));
-
- c_file->args.append("-DHAVE___CXA_THREAD_ATEXIT_IMPL");
- c_file->args.append("-D_LIBCPP_DISABLE_EXTERN_TEMPLATE");
- c_file->args.append("-D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS");
- c_file->args.append("-D_LIBCXXABI_BUILDING_LIBRARY");
- c_file->args.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS");
- c_file->args.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS");
-
- if (target_abi_is_musl(parent->zig_target->abi)) {
- c_file->args.append("-D_LIBCPP_HAS_MUSL_LIBC");
- }
-
- c_file->args.append("-I");
- c_file->args.append(cxxabi_include_path);
-
- c_file->args.append("-I");
- c_file->args.append(cxx_include_path);
-
- c_file->args.append("-O3");
- c_file->args.append("-DNDEBUG");
- if (target_supports_fpic(parent->zig_target)) {
- c_file->args.append("-fPIC");
- }
- c_file->args.append("-nostdinc++");
- c_file->args.append("-fstrict-aliasing");
- c_file->args.append("-funwind-tables");
- c_file->args.append("-D_DEBUG");
- c_file->args.append("-UNDEBUG");
- c_file->args.append("-std=c++11");
-
- c_source_files.append(c_file);
- }
-
-
- child_gen->c_source_files = c_source_files;
- codegen_build_and_link(child_gen);
- return buf_ptr(&child_gen->bin_file_output_path);
-}
-
-static const char *build_libcxx(CodeGen *parent, Stage2ProgressNode *progress_node) {
- CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "c++", progress_node);
- codegen_add_link_lib(child_gen, buf_create_from_str("c"));
-
- ZigList c_source_files = {0};
-
- const char *cxxabi_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxxabi" OS_SEP "include",
- buf_ptr(parent->zig_lib_dir)));
- const char *cxx_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "include",
- buf_ptr(parent->zig_lib_dir)));
-
- for (size_t i = 0; i < array_length(ZIG_LIBCXX_FILES); i += 1) {
- const char *rel_src_path = ZIG_LIBCXX_FILES[i];
-
- Buf *src_path_buf = buf_create_from_str(rel_src_path);
- if (parent->zig_target->os == OsWindows) {
- // filesystem stuff isn't supported on Windows
- if (buf_starts_with_str(src_path_buf, "src/filesystem/")) {
- continue;
- }
- } else {
- if (buf_starts_with_str(src_path_buf, "src/support/win32/")) {
- continue;
- }
- }
-
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "%s",
- buf_ptr(parent->zig_lib_dir), rel_src_path));
-
- c_file->args.append("-DNDEBUG");
- c_file->args.append("-D_LIBCPP_BUILDING_LIBRARY");
- c_file->args.append("-D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER");
- c_file->args.append("-DLIBCXX_BUILDING_LIBCXXABI");
- c_file->args.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS");
- c_file->args.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS");
-
- if (target_abi_is_musl(parent->zig_target->abi)) {
- c_file->args.append("-D_LIBCPP_HAS_MUSL_LIBC");
- }
-
- c_file->args.append("-I");
- c_file->args.append(cxx_include_path);
-
- c_file->args.append("-I");
- c_file->args.append(cxxabi_include_path);
-
- c_file->args.append("-O3");
- c_file->args.append("-DNDEBUG");
- if (target_supports_fpic(parent->zig_target)) {
- c_file->args.append("-fPIC");
- }
- c_file->args.append("-nostdinc++");
- c_file->args.append("-fvisibility-inlines-hidden");
- c_file->args.append("-std=c++14");
- c_file->args.append("-Wno-user-defined-literals");
-
- c_source_files.append(c_file);
- }
-
-
- child_gen->c_source_files = c_source_files;
- codegen_build_and_link(child_gen);
- return buf_ptr(&child_gen->bin_file_output_path);
-}
-
-static void add_msvcrt_os_dep(CodeGen *parent, CodeGen *child_gen, const char *src_path) {
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "%s",
- buf_ptr(parent->zig_lib_dir), src_path));
- c_file->args.append("-DHAVE_CONFIG_H");
- c_file->args.append("-D__LIBMSVCRT__");
-
- c_file->args.append("-I");
- c_file->args.append(path_from_libc(parent, "mingw" OS_SEP "include"));
-
- c_file->args.append("-std=gnu99");
- c_file->args.append("-D_CRTBLD");
- c_file->args.append("-D_WIN32_WINNT=0x0f00");
- c_file->args.append("-D__MSVCRT_VERSION__=0x700");
-
- c_file->args.append("-isystem");
- c_file->args.append(path_from_libc(parent, "include" OS_SEP "any-windows-any"));
-
- c_file->args.append("-g");
- c_file->args.append("-O2");
-
- child_gen->c_source_files.append(c_file);
-}
-
-static void add_mingwex_dep(CodeGen *parent, CodeGen *child_gen, const char *src_path) {
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "%s",
- buf_ptr(parent->zig_lib_dir), src_path));
- c_file->args.append("-DHAVE_CONFIG_H");
-
- c_file->args.append("-I");
- c_file->args.append(path_from_libc(parent, "mingw"));
-
- c_file->args.append("-I");
- c_file->args.append(path_from_libc(parent, "mingw" OS_SEP "include"));
-
- c_file->args.append("-std=gnu99");
- c_file->args.append("-D_CRTBLD");
- c_file->args.append("-D_WIN32_WINNT=0x0f00");
- c_file->args.append("-D__MSVCRT_VERSION__=0x700");
- c_file->args.append("-g");
- c_file->args.append("-O2");
-
- c_file->args.append("-isystem");
- c_file->args.append(path_from_libc(parent, "include" OS_SEP "any-windows-any"));
-
- child_gen->c_source_files.append(c_file);
-}
-
-static void add_mingw_uuid_dep(CodeGen *parent, CodeGen *child_gen, const char *src_path) {
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "%s",
- buf_ptr(parent->zig_lib_dir), src_path));
- c_file->args.append("-DHAVE_CONFIG_H");
-
- c_file->args.append("-I");
- c_file->args.append(path_from_libc(parent, "mingw"));
-
- c_file->args.append("-I");
- c_file->args.append(path_from_libc(parent, "mingw" OS_SEP "include"));
-
- c_file->args.append("-std=gnu99");
- c_file->args.append("-D_CRTBLD");
- c_file->args.append("-D_WIN32_WINNT=0x0f00");
- c_file->args.append("-D__MSVCRT_VERSION__=0x700");
- c_file->args.append("-g");
- c_file->args.append("-O2");
-
- c_file->args.append("-isystem");
- c_file->args.append(path_from_libc(parent, "include" OS_SEP "any-windows-any"));
-
- child_gen->c_source_files.append(c_file);
-}
-
-static const char *get_libc_crt_file(CodeGen *parent, const char *file, Stage2ProgressNode *progress_node) {
- if (parent->libc == nullptr && parent->zig_target->os == OsWindows) {
- if (strcmp(file, "crt2.o") == 0) {
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = buf_ptr(buf_sprintf(
- "%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "crt" OS_SEP "crtexe.c", buf_ptr(parent->zig_lib_dir)));
- mingw_add_cc_args(parent, c_file);
- c_file->args.append("-U__CRTDLL__");
- c_file->args.append("-D__MSVCRT__");
- // Uncomment these 3 things for crtu
- //c_file->args.append("-DUNICODE");
- //c_file->args.append("-D_UNICODE");
- //c_file->args.append("-DWPRFLAG=1");
- return build_libc_object(parent, "crt2", c_file, progress_node);
- } else if (strcmp(file, "dllcrt2.o") == 0) {
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = buf_ptr(buf_sprintf(
- "%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "crt" OS_SEP "crtdll.c", buf_ptr(parent->zig_lib_dir)));
- mingw_add_cc_args(parent, c_file);
- c_file->args.append("-U__CRTDLL__");
- c_file->args.append("-D__MSVCRT__");
- return build_libc_object(parent, "dllcrt2", c_file, progress_node);
- } else if (strcmp(file, "mingw32.lib") == 0) {
- CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "mingw32", progress_node);
-
- static const char *deps[] = {
- "mingw" OS_SEP "crt" OS_SEP "crt0_c.c",
- "mingw" OS_SEP "crt" OS_SEP "dll_argv.c",
- "mingw" OS_SEP "crt" OS_SEP "gccmain.c",
- "mingw" OS_SEP "crt" OS_SEP "natstart.c",
- "mingw" OS_SEP "crt" OS_SEP "pseudo-reloc-list.c",
- "mingw" OS_SEP "crt" OS_SEP "wildcard.c",
- "mingw" OS_SEP "crt" OS_SEP "charmax.c",
- "mingw" OS_SEP "crt" OS_SEP "crt0_w.c",
- "mingw" OS_SEP "crt" OS_SEP "dllargv.c",
- "mingw" OS_SEP "crt" OS_SEP "gs_support.c",
- "mingw" OS_SEP "crt" OS_SEP "_newmode.c",
- "mingw" OS_SEP "crt" OS_SEP "tlssup.c",
- "mingw" OS_SEP "crt" OS_SEP "xncommod.c",
- "mingw" OS_SEP "crt" OS_SEP "cinitexe.c",
- "mingw" OS_SEP "crt" OS_SEP "merr.c",
- "mingw" OS_SEP "crt" OS_SEP "usermatherr.c",
- "mingw" OS_SEP "crt" OS_SEP "pesect.c",
- "mingw" OS_SEP "crt" OS_SEP "udllargc.c",
- "mingw" OS_SEP "crt" OS_SEP "xthdloc.c",
- "mingw" OS_SEP "crt" OS_SEP "CRT_fp10.c",
- "mingw" OS_SEP "crt" OS_SEP "mingw_helpers.c",
- "mingw" OS_SEP "crt" OS_SEP "pseudo-reloc.c",
- "mingw" OS_SEP "crt" OS_SEP "udll_argv.c",
- "mingw" OS_SEP "crt" OS_SEP "xtxtmode.c",
- "mingw" OS_SEP "crt" OS_SEP "crt_handler.c",
- "mingw" OS_SEP "crt" OS_SEP "tlsthrd.c",
- "mingw" OS_SEP "crt" OS_SEP "tlsmthread.c",
- "mingw" OS_SEP "crt" OS_SEP "tlsmcrt.c",
- "mingw" OS_SEP "crt" OS_SEP "cxa_atexit.c",
- };
- for (size_t i = 0; i < array_length(deps); i += 1) {
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = path_from_libc(parent, deps[i]);
- c_file->args.append("-DHAVE_CONFIG_H");
- c_file->args.append("-D_SYSCRT=1");
- c_file->args.append("-DCRTDLL=1");
-
- c_file->args.append("-isystem");
- c_file->args.append(path_from_libc(parent, "include" OS_SEP "any-windows-any"));
-
- c_file->args.append("-isystem");
- c_file->args.append(path_from_libc(parent, "mingw" OS_SEP "include"));
-
- c_file->args.append("-std=gnu99");
- c_file->args.append("-D_CRTBLD");
- c_file->args.append("-D_WIN32_WINNT=0x0f00");
- c_file->args.append("-D__MSVCRT_VERSION__=0x700");
- c_file->args.append("-g");
- c_file->args.append("-O2");
-
- child_gen->c_source_files.append(c_file);
- }
- codegen_build_and_link(child_gen);
- return buf_ptr(&child_gen->bin_file_output_path);
- } else if (strcmp(file, "msvcrt-os.lib") == 0) {
- CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "msvcrt-os", progress_node);
-
- for (size_t i = 0; i < array_length(msvcrt_common_src); i += 1) {
- add_msvcrt_os_dep(parent, child_gen, msvcrt_common_src[i]);
- }
- if (parent->zig_target->arch == ZigLLVM_x86) {
- for (size_t i = 0; i < array_length(msvcrt_i386_src); i += 1) {
- add_msvcrt_os_dep(parent, child_gen, msvcrt_i386_src[i]);
- }
- } else {
- for (size_t i = 0; i < array_length(msvcrt_other_src); i += 1) {
- add_msvcrt_os_dep(parent, child_gen, msvcrt_other_src[i]);
- }
- }
- codegen_build_and_link(child_gen);
- return buf_ptr(&child_gen->bin_file_output_path);
- } else if (strcmp(file, "mingwex.lib") == 0) {
- CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "mingwex", progress_node);
-
- for (size_t i = 0; i < array_length(mingwex_generic_src); i += 1) {
- add_mingwex_dep(parent, child_gen, mingwex_generic_src[i]);
- }
- if (parent->zig_target->arch == ZigLLVM_x86 || parent->zig_target->arch == ZigLLVM_x86_64) {
- for (size_t i = 0; i < array_length(mingwex_x86_src); i += 1) {
- add_mingwex_dep(parent, child_gen, mingwex_x86_src[i]);
- }
- } else if (target_is_arm(parent->zig_target)) {
- if (target_arch_pointer_bit_width(parent->zig_target->arch) == 32) {
- for (size_t i = 0; i < array_length(mingwex_arm32_src); i += 1) {
- add_mingwex_dep(parent, child_gen, mingwex_arm32_src[i]);
- }
- } else {
- for (size_t i = 0; i < array_length(mingwex_arm64_src); i += 1) {
- add_mingwex_dep(parent, child_gen, mingwex_arm64_src[i]);
- }
- }
- } else {
- zig_unreachable();
- }
- codegen_build_and_link(child_gen);
- return buf_ptr(&child_gen->bin_file_output_path);
- } else if (strcmp(file, "uuid.lib") == 0) {
- CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "uuid", progress_node);
- for (size_t i = 0; i < array_length(mingw_uuid_src); i += 1) {
- add_mingw_uuid_dep(parent, child_gen, mingw_uuid_src[i]);
- }
- codegen_build_and_link(child_gen);
- return buf_ptr(&child_gen->bin_file_output_path);
- } else {
- zig_unreachable();
- }
- } else if (parent->libc == nullptr && target_is_glibc(parent->zig_target)) {
- if (strcmp(file, "crti.o") == 0) {
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = glibc_start_asm_path(parent, "crti.S");
- glibc_add_include_dirs(parent, c_file);
- c_file->args.append("-D_LIBC_REENTRANT");
- c_file->args.append("-include");
- c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-modules.h"));
- c_file->args.append("-DMODULE_NAME=libc");
- c_file->args.append("-Wno-nonportable-include-path");
- c_file->args.append("-include");
- c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-symbols.h"));
- c_file->args.append("-DTOP_NAMESPACE=glibc");
- c_file->args.append("-DASSEMBLER");
- c_file->args.append("-g");
- c_file->args.append("-Wa,--noexecstack");
- return build_libc_object(parent, "crti", c_file, progress_node);
- } else if (strcmp(file, "crtn.o") == 0) {
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = glibc_start_asm_path(parent, "crtn.S");
- glibc_add_include_dirs(parent, c_file);
- c_file->args.append("-D_LIBC_REENTRANT");
- c_file->args.append("-DMODULE_NAME=libc");
- c_file->args.append("-DTOP_NAMESPACE=glibc");
- c_file->args.append("-DASSEMBLER");
- c_file->args.append("-g");
- c_file->args.append("-Wa,--noexecstack");
- return build_libc_object(parent, "crtn", c_file, progress_node);
- } else if (strcmp(file, "start.os") == 0) {
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = glibc_start_asm_path(parent, "start.S");
- glibc_add_include_dirs(parent, c_file);
- c_file->args.append("-D_LIBC_REENTRANT");
- c_file->args.append("-include");
- c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-modules.h"));
- c_file->args.append("-DMODULE_NAME=libc");
- c_file->args.append("-Wno-nonportable-include-path");
- c_file->args.append("-include");
- c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-symbols.h"));
- c_file->args.append("-DPIC");
- c_file->args.append("-DSHARED");
- c_file->args.append("-DTOP_NAMESPACE=glibc");
- c_file->args.append("-DASSEMBLER");
- c_file->args.append("-g");
- c_file->args.append("-Wa,--noexecstack");
- return build_libc_object(parent, "start", c_file, progress_node);
- } else if (strcmp(file, "abi-note.o") == 0) {
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = path_from_libc(parent, "glibc" OS_SEP "csu" OS_SEP "abi-note.S");
- c_file->args.append("-I");
- c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "csu"));
- glibc_add_include_dirs(parent, c_file);
- c_file->args.append("-D_LIBC_REENTRANT");
- c_file->args.append("-DMODULE_NAME=libc");
- c_file->args.append("-DTOP_NAMESPACE=glibc");
- c_file->args.append("-DASSEMBLER");
- c_file->args.append("-g");
- c_file->args.append("-Wa,--noexecstack");
- return build_libc_object(parent, "abi-note", c_file, progress_node);
- } else if (strcmp(file, "Scrt1.o") == 0) {
- const char *start_os = get_libc_crt_file(parent, "start.os", progress_node);
- const char *abi_note_o = get_libc_crt_file(parent, "abi-note.o", progress_node);
- CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeObj, nullptr, "Scrt1", progress_node);
- codegen_add_object(child_gen, buf_create_from_str(start_os));
- codegen_add_object(child_gen, buf_create_from_str(abi_note_o));
- codegen_build_and_link(child_gen);
- return buf_ptr(&child_gen->bin_file_output_path);
- } else if (strcmp(file, "libc_nonshared.a") == 0) {
- CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "c_nonshared", progress_node);
- {
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = path_from_libc(parent, "glibc" OS_SEP "csu" OS_SEP "elf-init.c");
- c_file->args.append("-std=gnu11");
- c_file->args.append("-fgnu89-inline");
- c_file->args.append("-g");
- c_file->args.append("-O2");
- c_file->args.append("-fmerge-all-constants");
- c_file->args.append("-fno-stack-protector");
- c_file->args.append("-fmath-errno");
- c_file->args.append("-fno-stack-protector");
- c_file->args.append("-I");
- c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "csu"));
- glibc_add_include_dirs(parent, c_file);
- c_file->args.append("-DSTACK_PROTECTOR_LEVEL=0");
- c_file->args.append("-fPIC");
- c_file->args.append("-fno-stack-protector");
- c_file->args.append("-ftls-model=initial-exec");
- c_file->args.append("-D_LIBC_REENTRANT");
- c_file->args.append("-include");
- c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-modules.h"));
- c_file->args.append("-DMODULE_NAME=libc");
- c_file->args.append("-Wno-nonportable-include-path");
- c_file->args.append("-include");
- c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-symbols.h"));
- c_file->args.append("-DPIC");
- c_file->args.append("-DLIBC_NONSHARED=1");
- c_file->args.append("-DTOP_NAMESPACE=glibc");
- codegen_add_object(child_gen, buf_create_from_str(
- build_libc_object(parent, "elf-init", c_file, progress_node)));
- }
- static const struct {
- const char *name;
- const char *path;
- } deps[] = {
- {"atexit", "glibc" OS_SEP "stdlib" OS_SEP "atexit.c"},
- {"at_quick_exit", "glibc" OS_SEP "stdlib" OS_SEP "at_quick_exit.c"},
- {"stat", "glibc" OS_SEP "io" OS_SEP "stat.c"},
- {"fstat", "glibc" OS_SEP "io" OS_SEP "fstat.c"},
- {"lstat", "glibc" OS_SEP "io" OS_SEP "lstat.c"},
- {"stat64", "glibc" OS_SEP "io" OS_SEP "stat64.c"},
- {"fstat64", "glibc" OS_SEP "io" OS_SEP "fstat64.c"},
- {"lstat64", "glibc" OS_SEP "io" OS_SEP "lstat64.c"},
- {"fstatat", "glibc" OS_SEP "io" OS_SEP "fstatat.c"},
- {"fstatat64", "glibc" OS_SEP "io" OS_SEP "fstatat64.c"},
- {"mknod", "glibc" OS_SEP "io" OS_SEP "mknod.c"},
- {"mknodat", "glibc" OS_SEP "io" OS_SEP "mknodat.c"},
- {"pthread_atfork", "glibc" OS_SEP "nptl" OS_SEP "pthread_atfork.c"},
- {"stack_chk_fail_local", "glibc" OS_SEP "debug" OS_SEP "stack_chk_fail_local.c"},
- };
- for (size_t i = 0; i < array_length(deps); i += 1) {
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = path_from_libc(parent, deps[i].path);
- c_file->args.append("-std=gnu11");
- c_file->args.append("-fgnu89-inline");
- c_file->args.append("-g");
- c_file->args.append("-O2");
- c_file->args.append("-fmerge-all-constants");
- c_file->args.append("-fno-stack-protector");
- c_file->args.append("-fmath-errno");
- c_file->args.append("-ftls-model=initial-exec");
- c_file->args.append("-Wno-ignored-attributes");
- glibc_add_include_dirs(parent, c_file);
- c_file->args.append("-D_LIBC_REENTRANT");
- c_file->args.append("-include");
- c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-modules.h"));
- c_file->args.append("-DMODULE_NAME=libc");
- c_file->args.append("-Wno-nonportable-include-path");
- c_file->args.append("-include");
- c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-symbols.h"));
- c_file->args.append("-DPIC");
- c_file->args.append("-DLIBC_NONSHARED=1");
- c_file->args.append("-DTOP_NAMESPACE=glibc");
- codegen_add_object(child_gen, buf_create_from_str(
- build_libc_object(parent, deps[i].name, c_file, progress_node)));
- }
- codegen_build_and_link(child_gen);
- return buf_ptr(&child_gen->bin_file_output_path);
- } else {
- zig_unreachable();
- }
- } else if (parent->libc == nullptr && target_is_musl(parent->zig_target)) {
- if (strcmp(file, "crti.o") == 0) {
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = musl_start_asm_path(parent, "crti.s");
- musl_add_cc_args(parent, c_file, false);
- c_file->args.append("-Qunused-arguments");
- return build_libc_object(parent, "crti", c_file, progress_node);
- } else if (strcmp(file, "crtn.o") == 0) {
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = musl_start_asm_path(parent, "crtn.s");
- c_file->args.append("-Qunused-arguments");
- musl_add_cc_args(parent, c_file, false);
- return build_libc_object(parent, "crtn", c_file, progress_node);
- } else if (strcmp(file, "crt1.o") == 0) {
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = path_from_libc(parent, "musl" OS_SEP "crt" OS_SEP "crt1.c");
- musl_add_cc_args(parent, c_file, false);
- c_file->args.append("-fno-stack-protector");
- c_file->args.append("-DCRT");
- return build_libc_object(parent, "crt1", c_file, progress_node);
- } else if (strcmp(file, "Scrt1.o") == 0) {
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = path_from_libc(parent, "musl" OS_SEP "crt" OS_SEP "Scrt1.c");
- musl_add_cc_args(parent, c_file, false);
- c_file->args.append("-fPIC");
- c_file->args.append("-fno-stack-protector");
- c_file->args.append("-DCRT");
- return build_libc_object(parent, "Scrt1", c_file, progress_node);
- } else {
- zig_unreachable();
- }
- } else {
- assert(parent->libc != nullptr);
- Buf *out_buf = buf_alloc();
- os_path_join(buf_create_from_mem(parent->libc->crt_dir, parent->libc->crt_dir_len),
- buf_create_from_str(file), out_buf);
- return buf_ptr(out_buf);
- }
-}
-
-static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path, OutType child_out_type,
- Stage2ProgressNode *progress_node)
-{
- CodeGen *child_gen = create_child_codegen(parent_gen, full_path, child_out_type, parent_gen->libc, aname,
- progress_node);
-
- // This is so that compiler_rt and libc.zig libraries know whether they
- // will eventually be linked with libc. They make different decisions
- // about what to export depending on whether libc is linked.
- if (parent_gen->libc_link_lib != nullptr) {
- LinkLib *new_link_lib = codegen_add_link_lib(child_gen, parent_gen->libc_link_lib->name);
- new_link_lib->provided_explicitly = parent_gen->libc_link_lib->provided_explicitly;
- }
-
- // Override the inherited build mode parameter
- if (!parent_gen->is_test_build) {
- switch (parent_gen->build_mode) {
- case BuildModeDebug:
- case BuildModeFastRelease:
- case BuildModeSafeRelease:
- child_gen->build_mode = BuildModeFastRelease;
- break;
- case BuildModeSmallRelease:
- break;
- }
- }
-
- child_gen->function_sections = true;
- child_gen->want_stack_check = WantStackCheckDisabled;
-
- codegen_build_and_link(child_gen);
- return &child_gen->bin_file_output_path;
-}
-
-static Buf *build_compiler_rt(CodeGen *parent_gen, OutType child_out_type, Stage2ProgressNode *progress_node) {
- Buf *full_path = buf_alloc();
- os_path_join(parent_gen->zig_std_special_dir, buf_create_from_str("compiler_rt.zig"), full_path);
-
- return build_a_raw(parent_gen, "compiler_rt", full_path, child_out_type, progress_node);
-}
-
-static Buf *build_c(CodeGen *parent_gen, OutType child_out_type, Stage2ProgressNode *progress_node) {
- Buf *full_path = buf_alloc();
- os_path_join(parent_gen->zig_std_special_dir, buf_create_from_str("c.zig"), full_path);
-
- return build_a_raw(parent_gen, "c", full_path, child_out_type, progress_node);
-}
-
-static const char *get_darwin_arch_string(const ZigTarget *t) {
- switch (t->arch) {
- case ZigLLVM_aarch64:
- return "arm64";
- case ZigLLVM_thumb:
- case ZigLLVM_arm:
- return "arm";
- case ZigLLVM_ppc:
- return "ppc";
- case ZigLLVM_ppc64:
- return "ppc64";
- case ZigLLVM_ppc64le:
- return "ppc64le";
- default:
- return ZigLLVMGetArchTypeName(t->arch);
- }
-}
-
-
-static const char *getLDMOption(const ZigTarget *t) {
- switch (t->arch) {
- case ZigLLVM_x86:
- return "elf_i386";
- case ZigLLVM_aarch64:
- return "aarch64linux";
- case ZigLLVM_aarch64_be:
- return "aarch64_be_linux";
- case ZigLLVM_arm:
- case ZigLLVM_thumb:
- return "armelf_linux_eabi";
- case ZigLLVM_armeb:
- case ZigLLVM_thumbeb:
- return "armebelf_linux_eabi";
- case ZigLLVM_ppc:
- return "elf32ppclinux";
- case ZigLLVM_ppc64:
- return "elf64ppc";
- case ZigLLVM_ppc64le:
- return "elf64lppc";
- case ZigLLVM_sparc:
- case ZigLLVM_sparcel:
- return "elf32_sparc";
- case ZigLLVM_sparcv9:
- return "elf64_sparc";
- case ZigLLVM_mips:
- return "elf32btsmip";
- case ZigLLVM_mipsel:
- return "elf32ltsmip";
- return "elf64btsmip";
- case ZigLLVM_mips64el:
- return "elf64ltsmip";
- case ZigLLVM_systemz:
- return "elf64_s390";
- case ZigLLVM_x86_64:
- if (t->abi == ZigLLVM_GNUX32) {
- return "elf32_x86_64";
- }
- // Any target elf will use the freebsd osabi if suffixed with "_fbsd".
- if (t->os == OsFreeBSD) {
- return "elf_x86_64_fbsd";
- }
- return "elf_x86_64";
- case ZigLLVM_riscv32:
- return "elf32lriscv";
- case ZigLLVM_riscv64:
- return "elf64lriscv";
- default:
- zig_unreachable();
- }
-}
-
-static void add_rpath(LinkJob *lj, Buf *rpath) {
- if (lj->rpath_table.maybe_get(rpath) != nullptr)
- return;
-
- lj->args.append("-rpath");
- lj->args.append(buf_ptr(rpath));
-
- lj->rpath_table.put(rpath, true);
-}
-
-static void add_glibc_libs(LinkJob *lj) {
- Error err;
- ZigGLibCAbi *glibc_abi;
- if ((err = glibc_load_metadata(&glibc_abi, lj->codegen->zig_lib_dir, true))) {
- fprintf(stderr, "%s\n", err_str(err));
- exit(1);
- }
-
- Buf *artifact_dir;
- if ((err = glibc_build_dummies_and_maps(lj->codegen, glibc_abi, lj->codegen->zig_target,
- &artifact_dir, true, lj->build_dep_prog_node)))
- {
- fprintf(stderr, "%s\n", err_str(err));
- exit(1);
- }
-
- size_t lib_count = glibc_lib_count();
- for (size_t i = 0; i < lib_count; i += 1) {
- const ZigGLibCLib *lib = glibc_lib_enum(i);
- Buf *so_path = buf_sprintf("%s" OS_SEP "lib%s.so.%d.0.0", buf_ptr(artifact_dir), lib->name, lib->sover);
- lj->args.append(buf_ptr(so_path));
- }
-}
-
-static void construct_linker_job_elf(LinkJob *lj) {
- CodeGen *g = lj->codegen;
-
- lj->args.append("-error-limit=0");
-
- if (g->out_type == OutTypeExe) {
- lj->args.append("-z");
- size_t stack_size = (g->stack_size_override == 0) ? 16777216 : g->stack_size_override;
- lj->args.append(buf_ptr(buf_sprintf("stack-size=%" ZIG_PRI_usize, stack_size)));
- }
-
- if (g->linker_script) {
- lj->args.append("-T");
- lj->args.append(g->linker_script);
- }
-
- switch (g->linker_gc_sections) {
- case OptionalBoolNull:
- if (g->out_type != OutTypeObj) {
- lj->args.append("--gc-sections");
- }
- break;
- case OptionalBoolTrue:
- lj->args.append("--gc-sections");
- break;
- case OptionalBoolFalse:
- break;
- }
-
- if (g->link_eh_frame_hdr) {
- lj->args.append("--eh-frame-hdr");
- }
-
- if (g->linker_rdynamic) {
- lj->args.append("--export-dynamic");
- }
-
- if (g->linker_optimization != nullptr) {
- lj->args.append(buf_ptr(g->linker_optimization));
- }
-
- if (g->linker_z_nodelete) {
- lj->args.append("-z");
- lj->args.append("nodelete");
- }
- if (g->linker_z_defs) {
- lj->args.append("-z");
- lj->args.append("defs");
- }
-
- lj->args.append("-m");
- lj->args.append(getLDMOption(g->zig_target));
-
- bool is_lib = g->out_type == OutTypeLib;
- bool is_dyn_lib = g->is_dynamic && is_lib;
- if (!g->have_dynamic_link) {
- if (g->zig_target->arch == ZigLLVM_arm || g->zig_target->arch == ZigLLVM_armeb ||
- g->zig_target->arch == ZigLLVM_thumb || g->zig_target->arch == ZigLLVM_thumbeb)
- {
- lj->args.append("-Bstatic");
- } else {
- lj->args.append("-static");
- }
- } else if (is_dyn_lib) {
- lj->args.append("-shared");
- }
-
- if (target_requires_pie(g->zig_target) && g->out_type == OutTypeExe) {
- lj->args.append("-pie");
- }
-
- assert(buf_len(&g->bin_file_output_path) != 0);
- lj->args.append("-o");
- lj->args.append(buf_ptr(&g->bin_file_output_path));
-
- if (lj->link_in_crt) {
- const char *crt1o;
- if (g->zig_target->os == OsNetBSD) {
- crt1o = "crt0.o";
- } else if (target_is_android(g->zig_target)) {
- if (g->have_dynamic_link) {
- crt1o = "crtbegin_dynamic.o";
- } else {
- crt1o = "crtbegin_static.o";
- }
- } else if (!g->have_dynamic_link) {
- crt1o = "crt1.o";
- } else {
- crt1o = "Scrt1.o";
- }
- lj->args.append(get_libc_crt_file(g, crt1o, lj->build_dep_prog_node));
- if (target_libc_needs_crti_crtn(g->zig_target)) {
- lj->args.append(get_libc_crt_file(g, "crti.o", lj->build_dep_prog_node));
- }
- }
-
- for (size_t i = 0; i < g->rpath_list.length; i += 1) {
- Buf *rpath = g->rpath_list.at(i);
- add_rpath(lj, rpath);
- }
- if (g->each_lib_rpath) {
- for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
- const char *lib_dir = g->lib_dirs.at(i);
- for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
- LinkLib *link_lib = g->link_libs_list.at(i);
- if (buf_eql_str(link_lib->name, "c")) {
- continue;
- }
- bool does_exist;
- Buf *test_path = buf_sprintf("%s/lib%s.so", lib_dir, buf_ptr(link_lib->name));
- if (os_file_exists(test_path, &does_exist) != ErrorNone) {
- zig_panic("link: unable to check if file exists: %s", buf_ptr(test_path));
- }
- if (does_exist) {
- add_rpath(lj, buf_create_from_str(lib_dir));
- break;
- }
- }
- }
- }
-
- for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
- const char *lib_dir = g->lib_dirs.at(i);
- lj->args.append("-L");
- lj->args.append(lib_dir);
- }
-
- if (g->libc_link_lib != nullptr) {
- if (g->libc != nullptr) {
- lj->args.append("-L");
- lj->args.append(buf_ptr(buf_create_from_mem(g->libc->crt_dir, g->libc->crt_dir_len)));
- }
-
- if (g->have_dynamic_link && (is_dyn_lib || g->out_type == OutTypeExe)) {
- assert(g->zig_target->dynamic_linker != nullptr);
- lj->args.append("-dynamic-linker");
- lj->args.append(g->zig_target->dynamic_linker);
- }
- }
-
- if (is_dyn_lib) {
- Buf *soname = (g->override_soname == nullptr) ?
- buf_sprintf("lib%s.so.%" ZIG_PRI_usize, buf_ptr(g->root_out_name), g->version_major) :
- g->override_soname;
- lj->args.append("-soname");
- lj->args.append(buf_ptr(soname));
-
- if (g->version_script_path != nullptr) {
- lj->args.append("-version-script");
- lj->args.append(buf_ptr(g->version_script_path));
- }
- }
-
- // .o files
- for (size_t i = 0; i < g->link_objects.length; i += 1) {
- lj->args.append((const char *)buf_ptr(g->link_objects.at(i)));
- }
-
- if (!g->is_dummy_so && (g->out_type == OutTypeExe || is_dyn_lib)) {
- if (g->libc_link_lib == nullptr) {
- Buf *libc_a_path = build_c(g, OutTypeLib, lj->build_dep_prog_node);
- lj->args.append(buf_ptr(libc_a_path));
- }
-
- Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib, lj->build_dep_prog_node);
- lj->args.append(buf_ptr(compiler_rt_o_path));
- }
-
- // libraries
- for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
- LinkLib *link_lib = g->link_libs_list.at(i);
- if (buf_eql_str(link_lib->name, "c")) {
- // libc is linked specially
- continue;
- }
- if (target_is_libcpp_lib_name(g->zig_target, buf_ptr(link_lib->name))) {
- // libc++ is linked specially
- continue;
- }
- if (g->libc == nullptr && target_is_libc_lib_name(g->zig_target, buf_ptr(link_lib->name))) {
- // these libraries are always linked below when targeting glibc
- continue;
- }
- Buf *arg;
- if (buf_starts_with_str(link_lib->name, "/") || buf_ends_with_str(link_lib->name, ".a") ||
- buf_ends_with_str(link_lib->name, ".so"))
- {
- arg = link_lib->name;
- } else {
- arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
- }
- lj->args.append(buf_ptr(arg));
- }
-
- // libc++ dep
- if (g->libcpp_link_lib != nullptr && g->out_type != OutTypeObj) {
- lj->args.append(build_libcxxabi(g, lj->build_dep_prog_node));
- lj->args.append(build_libcxx(g, lj->build_dep_prog_node));
- }
-
- // libc dep
- if (g->libc_link_lib != nullptr && g->out_type != OutTypeObj) {
- if (g->libc != nullptr) {
- if (!g->have_dynamic_link) {
- lj->args.append("--start-group");
- lj->args.append("-lc");
- lj->args.append("-lm");
- lj->args.append("--end-group");
- } else {
- lj->args.append("-lc");
- lj->args.append("-lm");
- }
-
- if (g->zig_target->os == OsFreeBSD ||
- g->zig_target->os == OsNetBSD)
- {
- lj->args.append("-lpthread");
- }
- } else if (target_is_glibc(g->zig_target)) {
- lj->args.append(build_libunwind(g, lj->build_dep_prog_node));
- add_glibc_libs(lj);
- lj->args.append(get_libc_crt_file(g, "libc_nonshared.a", lj->build_dep_prog_node));
- } else if (target_is_musl(g->zig_target)) {
- lj->args.append(build_libunwind(g, lj->build_dep_prog_node));
- lj->args.append(build_musl(g, lj->build_dep_prog_node));
- } else if (g->libcpp_link_lib != nullptr) {
- lj->args.append(build_libunwind(g, lj->build_dep_prog_node));
- } else {
- zig_unreachable();
- }
- }
-
- // crt end
- if (lj->link_in_crt) {
- if (target_is_android(g->zig_target)) {
- lj->args.append(get_libc_crt_file(g, "crtend_android.o", lj->build_dep_prog_node));
- } else if (target_libc_needs_crti_crtn(g->zig_target)) {
- lj->args.append(get_libc_crt_file(g, "crtn.o", lj->build_dep_prog_node));
- }
- }
-
- switch (g->linker_allow_shlib_undefined) {
- case OptionalBoolNull:
- if (!g->zig_target->is_native_os) {
- lj->args.append("--allow-shlib-undefined");
- }
- break;
- case OptionalBoolFalse:
- break;
- case OptionalBoolTrue:
- lj->args.append("--allow-shlib-undefined");
- break;
- }
- switch (g->linker_bind_global_refs_locally) {
- case OptionalBoolNull:
- case OptionalBoolFalse:
- break;
- case OptionalBoolTrue:
- lj->args.append("-Bsymbolic");
- break;
- }
-}
-
-static void construct_linker_job_wasm(LinkJob *lj) {
- CodeGen *g = lj->codegen;
-
- lj->args.append("-error-limit=0");
- // Increase the default stack size to a more reasonable value of 1MB instead of
- // the default of 1 Wasm page being 64KB, unless overriden by the user.
- size_t stack_size = (g->stack_size_override == 0) ? 1048576 : g->stack_size_override;
- lj->args.append("-z");
- lj->args.append(buf_ptr(buf_sprintf("stack-size=%" ZIG_PRI_usize, stack_size)));
-
- // put stack before globals so that stack overflow results in segfault immediately before corrupting globals
- // see https://github.com/ziglang/zig/issues/4496
- lj->args.append("--stack-first");
-
- if (g->out_type != OutTypeExe) {
- lj->args.append("--no-entry"); // So lld doesn't look for _start.
-
- // If there are any C source files we cannot rely on individual exports.
- if (g->c_source_files.length != 0) {
- lj->args.append("--export-all");
- } else {
- auto export_it = g->exported_symbol_names.entry_iterator();
- decltype(g->exported_symbol_names)::Entry *curr_entry = nullptr;
- while ((curr_entry = export_it.next()) != nullptr) {
- Buf *arg = buf_sprintf("--export=%s", buf_ptr(curr_entry->key));
- lj->args.append(buf_ptr(arg));
- }
- }
- }
- lj->args.append("--allow-undefined");
- lj->args.append("-o");
- lj->args.append(buf_ptr(&g->bin_file_output_path));
-
- // .o files
- for (size_t i = 0; i < g->link_objects.length; i += 1) {
- lj->args.append((const char *)buf_ptr(g->link_objects.at(i)));
- }
-
- if (g->out_type != OutTypeObj) {
- Buf *libc_o_path = build_c(g, OutTypeObj, lj->build_dep_prog_node);
- lj->args.append(buf_ptr(libc_o_path));
-
- Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeObj, lj->build_dep_prog_node);
- lj->args.append(buf_ptr(compiler_rt_o_path));
- }
-}
-
-static void coff_append_machine_arg(CodeGen *g, ZigList *list) {
- if (g->zig_target->arch == ZigLLVM_x86) {
- list->append("-MACHINE:X86");
- } else if (g->zig_target->arch == ZigLLVM_x86_64) {
- list->append("-MACHINE:X64");
- } else if (target_is_arm(g->zig_target)) {
- if (target_arch_pointer_bit_width(g->zig_target->arch) == 32) {
- list->append("-MACHINE:ARM");
- } else {
- list->append("-MACHINE:ARM64");
- }
- }
-}
-
-static void link_diag_callback(void *context, const char *ptr, size_t len) {
- Buf *diag = reinterpret_cast(context);
- buf_append_mem(diag, ptr, len);
-}
-
-static bool zig_lld_link(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count,
- Buf *diag)
-{
- Buf *stdout_diag = buf_alloc();
- buf_resize(diag, 0);
- bool result = ZigLLDLink(oformat, args, arg_count, link_diag_callback, stdout_diag, diag);
- buf_destroy(stdout_diag);
- return result;
-}
-
-static void add_uefi_link_args(LinkJob *lj) {
- lj->args.append("-BASE:0");
- lj->args.append("-ENTRY:EfiMain");
- lj->args.append("-OPT:REF");
- lj->args.append("-SAFESEH:NO");
- lj->args.append("-MERGE:.rdata=.data");
- lj->args.append("-ALIGN:32");
- lj->args.append("-NODEFAULTLIB");
- lj->args.append("-SECTION:.xdata,D");
-}
-
-static void add_msvc_link_args(LinkJob *lj, bool is_library) {
- CodeGen *g = lj->codegen;
-
- bool is_dynamic = g->is_dynamic;
- const char *lib_str = is_dynamic ? "" : "lib";
- const char *d_str = (g->build_mode == BuildModeDebug) ? "d" : "";
-
- if (!is_dynamic) {
- Buf *cmt_lib_name = buf_sprintf("libcmt%s.lib", d_str);
- lj->args.append(buf_ptr(cmt_lib_name));
- } else {
- Buf *msvcrt_lib_name = buf_sprintf("msvcrt%s.lib", d_str);
- lj->args.append(buf_ptr(msvcrt_lib_name));
- }
-
- Buf *vcruntime_lib_name = buf_sprintf("%svcruntime%s.lib", lib_str, d_str);
- lj->args.append(buf_ptr(vcruntime_lib_name));
-
- Buf *crt_lib_name = buf_sprintf("%sucrt%s.lib", lib_str, d_str);
- lj->args.append(buf_ptr(crt_lib_name));
-
- //Visual C++ 2015 Conformance Changes
- //https://msdn.microsoft.com/en-us/library/bb531344.aspx
- lj->args.append("legacy_stdio_definitions.lib");
-
- // msvcrt depends on kernel32 and ntdll
- lj->args.append("kernel32.lib");
- lj->args.append("ntdll.lib");
-}
-
-static void print_zig_cc_cmd(ZigList *args) {
- for (size_t arg_i = 0; arg_i < args->length; arg_i += 1) {
- const char *space_str = (arg_i == 0) ? "" : " ";
- fprintf(stderr, "%s%s", space_str, args->at(arg_i));
- }
- fprintf(stderr, "\n");
-}
-
-static const char *get_def_lib(CodeGen *parent, const char *name, Buf *def_in_file) {
- Error err;
-
- Buf *self_exe_path = buf_alloc();
- if ((err = os_self_exe_path(self_exe_path))) {
- fprintf(stderr, "Unable to get self exe path: %s\n", err_str(err));
- exit(1);
- }
- Buf *compiler_id;
- if ((err = get_compiler_id(&compiler_id))) {
- fprintf(stderr, "Unable to get compiler id: %s\n", err_str(err));
- exit(1);
- }
-
- Buf *cache_dir = get_global_cache_dir();
- Buf *o_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR, buf_ptr(cache_dir));
- Buf *manifest_dir = buf_sprintf("%s" OS_SEP CACHE_HASH_SUBDIR, buf_ptr(cache_dir));
-
- Buf *def_include_dir = buf_sprintf("%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "def-include",
- buf_ptr(parent->zig_lib_dir));
-
- CacheHash *cache_hash = heap::c_allocator.create();
- cache_init(cache_hash, manifest_dir);
-
- cache_buf(cache_hash, compiler_id);
- cache_file(cache_hash, def_in_file);
- cache_buf(cache_hash, def_include_dir);
- cache_int(cache_hash, parent->zig_target->arch);
-
- Buf digest = BUF_INIT;
- buf_resize(&digest, 0);
- if ((err = cache_hit(cache_hash, &digest))) {
- if (err != ErrorInvalidFormat) {
- if (err == ErrorCacheUnavailable) {
- // already printed error
- } else {
- fprintf(stderr, "unable to check cache when processing .def.in file: %s\n", err_str(err));
- }
- exit(1);
- }
- }
-
- Buf *artifact_dir;
- Buf *lib_final_path;
- Buf *final_lib_basename = buf_sprintf("%s.lib", name);
-
- bool is_cache_miss = (buf_len(&digest) == 0);
- if (is_cache_miss) {
- if ((err = cache_final(cache_hash, &digest))) {
- fprintf(stderr, "Unable to finalize cache hash: %s\n", err_str(err));
- exit(1);
- }
- artifact_dir = buf_alloc();
- os_path_join(o_dir, &digest, artifact_dir);
- if ((err = os_make_path(artifact_dir))) {
- fprintf(stderr, "Unable to create output directory '%s': %s",
- buf_ptr(artifact_dir), err_str(err));
- exit(1);
- }
- Buf *final_def_basename = buf_sprintf("%s.def", name);
- Buf *def_final_path = buf_alloc();
- os_path_join(artifact_dir, final_def_basename, def_final_path);
-
- ZigList args = {};
- args.append(buf_ptr(self_exe_path));
- args.append("clang");
- args.append("-x");
- args.append("c");
- args.append(buf_ptr(def_in_file));
- args.append("-Wp,-w");
- args.append("-undef");
- args.append("-P");
- args.append("-I");
- args.append(buf_ptr(def_include_dir));
- if (target_is_arm(parent->zig_target)) {
- if (target_arch_pointer_bit_width(parent->zig_target->arch) == 32) {
- args.append("-DDEF_ARM32");
- } else {
- args.append("-DDEF_ARM64");
- }
- } else if (parent->zig_target->arch == ZigLLVM_x86) {
- args.append("-DDEF_I386");
- } else if (parent->zig_target->arch == ZigLLVM_x86_64) {
- args.append("-DDEF_X64");
- } else {
- zig_unreachable();
- }
- args.append("-E");
- args.append("-o");
- args.append(buf_ptr(def_final_path));
-
- if (parent->verbose_cc) {
- print_zig_cc_cmd(&args);
- }
- Termination term;
- os_spawn_process(args, &term);
- if (term.how != TerminationIdClean || term.code != 0) {
- fprintf(stderr, "\nThe following command failed:\n");
- print_zig_cc_cmd(&args);
- exit(1);
- }
-
- lib_final_path = buf_alloc();
- os_path_join(artifact_dir, final_lib_basename, lib_final_path);
-
- if (ZigLLVMWriteImportLibrary(buf_ptr(def_final_path),
- parent->zig_target->arch,
- buf_ptr(lib_final_path),
- /* kill_at */ true))
- {
- zig_panic("link: could not emit %s", buf_ptr(lib_final_path));
- }
- } else {
- // cache hit
- artifact_dir = buf_alloc();
- os_path_join(o_dir, &digest, artifact_dir);
- lib_final_path = buf_alloc();
- os_path_join(artifact_dir, final_lib_basename, lib_final_path);
- }
- parent->caches_to_release.append(cache_hash);
-
- return buf_ptr(lib_final_path);
-}
-
-static bool is_linking_system_lib(CodeGen *g, const char *name) {
- for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) {
- LinkLib *link_lib = g->link_libs_list.at(lib_i);
- if (buf_eql_str(link_lib->name, name)) {
- return true;
- }
- }
- return false;
-}
-
-static Error find_mingw_lib_def(LinkJob *lj, const char *name, Buf *out_path) {
- CodeGen *g = lj->codegen;
- Buf override_path = BUF_INIT;
- Error err;
-
- char const *lib_path = nullptr;
- if (g->zig_target->arch == ZigLLVM_x86) {
- lib_path = "lib32";
- } else if (g->zig_target->arch == ZigLLVM_x86_64) {
- lib_path = "lib64";
- } else if (target_is_arm(g->zig_target)) {
- const bool is_32 = target_arch_pointer_bit_width(g->zig_target->arch) == 32;
- lib_path = is_32 ? "libarm32" : "libarm64";
- } else {
- zig_unreachable();
- }
-
- // Try the archtecture-specific path first
- buf_resize(&override_path, 0);
- buf_appendf(&override_path, "%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "%s" OS_SEP "%s.def", buf_ptr(g->zig_lib_dir), lib_path, name);
-
- bool does_exist;
- if ((err = os_file_exists(&override_path, &does_exist)) != ErrorNone) {
- return err;
- }
-
- if (!does_exist) {
- // Try the generic version
- buf_resize(&override_path, 0);
- buf_appendf(&override_path, "%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "lib-common" OS_SEP "%s.def", buf_ptr(g->zig_lib_dir), name);
-
- if ((err = os_file_exists(&override_path, &does_exist)) != ErrorNone) {
- return err;
- }
- }
-
- if (!does_exist) {
- // Try the generic version and preprocess it
- buf_resize(&override_path, 0);
- buf_appendf(&override_path, "%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "lib-common" OS_SEP "%s.def.in", buf_ptr(g->zig_lib_dir), name);
-
- if ((err = os_file_exists(&override_path, &does_exist)) != ErrorNone) {
- return err;
- }
- }
-
- if (!does_exist) {
- return ErrorFileNotFound;
- }
-
- buf_init_from_buf(out_path, &override_path);
- return ErrorNone;
-}
-
-static void add_mingw_link_args(LinkJob *lj, bool is_library) {
- CodeGen *g = lj->codegen;
-
- lj->args.append("-lldmingw");
-
- bool is_dll = g->out_type == OutTypeLib && g->is_dynamic;
-
- if (g->zig_target->arch == ZigLLVM_x86) {
- lj->args.append("-ALTERNATENAME:__image_base__=___ImageBase");
- } else {
- lj->args.append("-ALTERNATENAME:__image_base__=__ImageBase");
- }
-
- if (is_dll) {
- lj->args.append(get_libc_crt_file(g, "dllcrt2.o", lj->build_dep_prog_node));
- } else {
- lj->args.append(get_libc_crt_file(g, "crt2.o", lj->build_dep_prog_node));
- }
-
- lj->args.append(get_libc_crt_file(g, "mingw32.lib", lj->build_dep_prog_node));
- lj->args.append(get_libc_crt_file(g, "mingwex.lib", lj->build_dep_prog_node));
- lj->args.append(get_libc_crt_file(g, "msvcrt-os.lib", lj->build_dep_prog_node));
-
- for (size_t def_i = 0; def_i < array_length(mingw_def_list); def_i += 1) {
- const char *name = mingw_def_list[def_i].name;
- const bool always_link = mingw_def_list[def_i].always_link;
-
- if (always_link || is_linking_system_lib(g, name)) {
- Buf lib_path = BUF_INIT;
- Error err = find_mingw_lib_def(lj, name, &lib_path);
-
- if (err == ErrorFileNotFound) {
- zig_panic("link: could not find .def file to build %s\n", name);
- } else if (err != ErrorNone) {
- zig_panic("link: unable to check if .def file for %s exists: %s",
- name, err_str(err));
- }
-
- lj->args.append(get_def_lib(g, name, &lib_path));
- }
- }
-}
-
-static void add_win_link_args(LinkJob *lj, bool is_library, bool *have_windows_dll_import_libs) {
- if (lj->link_in_crt) {
- if (target_abi_is_gnu(lj->codegen->zig_target->abi)) {
- *have_windows_dll_import_libs = true;
- add_mingw_link_args(lj, is_library);
- } else {
- add_msvc_link_args(lj, is_library);
- }
- } else {
- lj->args.append("-NODEFAULTLIB");
- if (!is_library) {
- if (lj->codegen->have_winmain) {
- lj->args.append("-ENTRY:WinMain");
- } else if (lj->codegen->have_wwinmain) {
- lj->args.append("-ENTRY:wWinMain");
- } else if (lj->codegen->have_wwinmain_crt_startup) {
- lj->args.append("-ENTRY:wWinMainCRTStartup");
- } else {
- lj->args.append("-ENTRY:WinMainCRTStartup");
- }
- }
- }
-}
-
-static bool is_mingw_link_lib(Buf *name) {
- for (size_t def_i = 0; def_i < array_length(mingw_def_list); def_i += 1) {
- if (buf_eql_str_ignore_case(name, mingw_def_list[def_i].name)) {
- return true;
- }
- }
- return false;
-}
-static void construct_linker_job_coff(LinkJob *lj) {
- Error err;
- CodeGen *g = lj->codegen;
-
- lj->args.append("-ERRORLIMIT:0");
-
- lj->args.append("-NOLOGO");
-
- if (!g->strip_debug_symbols) {
- lj->args.append("-DEBUG");
- }
-
- if (g->out_type == OutTypeExe) {
- // TODO compile time stack upper bound detection
- size_t stack_size = (g->stack_size_override == 0) ? 16777216 : g->stack_size_override;
- lj->args.append(buf_ptr(buf_sprintf("-STACK:%" ZIG_PRI_usize, stack_size)));
- }
-
- coff_append_machine_arg(g, &lj->args);
-
- bool is_library = g->out_type == OutTypeLib;
- if (is_library && g->is_dynamic) {
- lj->args.append("-DLL");
- }
-
- lj->args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(&g->bin_file_output_path))));
-
- if (g->libc_link_lib != nullptr && g->libc != nullptr) {
- Buf *buff0 = buf_create_from_str("-LIBPATH:");
- buf_append_mem(buff0, g->libc->crt_dir, g->libc->crt_dir_len);
- lj->args.append(buf_ptr(buff0));
-
- if (target_abi_is_gnu(g->zig_target->abi)) {
- Buf *buff1 = buf_create_from_str("-LIBPATH:");
- buf_append_mem(buff1, g->libc->sys_include_dir, g->libc->sys_include_dir_len);
- lj->args.append(buf_ptr(buff1));
-
- Buf *buff2 = buf_create_from_str("-LIBPATH:");
- buf_append_mem(buff2, g->libc->include_dir, g->libc->include_dir_len);
- lj->args.append(buf_ptr(buff2));
- } else {
- Buf *buff1 = buf_create_from_str("-LIBPATH:");
- buf_append_mem(buff1, g->libc->msvc_lib_dir, g->libc->msvc_lib_dir_len);
- lj->args.append(buf_ptr(buff1));
-
- Buf *buff2 = buf_create_from_str("-LIBPATH:");
- buf_append_mem(buff2, g->libc->kernel32_lib_dir, g->libc->kernel32_lib_dir_len);
- lj->args.append(buf_ptr(buff2));
- }
- }
-
- for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
- const char *lib_dir = g->lib_dirs.at(i);
- lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", lib_dir)));
- }
-
- for (size_t i = 0; i < g->link_objects.length; i += 1) {
- lj->args.append((const char *)buf_ptr(g->link_objects.at(i)));
- }
-
- bool have_windows_dll_import_libs = false;
- switch (detect_subsystem(g)) {
- case TargetSubsystemAuto:
- if (g->zig_target->os == OsUefi) {
- add_uefi_link_args(lj);
- } else {
- add_win_link_args(lj, is_library, &have_windows_dll_import_libs);
- }
- break;
- case TargetSubsystemConsole:
- lj->args.append("-SUBSYSTEM:console");
- add_win_link_args(lj, is_library, &have_windows_dll_import_libs);
- break;
- case TargetSubsystemEfiApplication:
- lj->args.append("-SUBSYSTEM:efi_application");
- add_uefi_link_args(lj);
- break;
- case TargetSubsystemEfiBootServiceDriver:
- lj->args.append("-SUBSYSTEM:efi_boot_service_driver");
- add_uefi_link_args(lj);
- break;
- case TargetSubsystemEfiRom:
- lj->args.append("-SUBSYSTEM:efi_rom");
- add_uefi_link_args(lj);
- break;
- case TargetSubsystemEfiRuntimeDriver:
- lj->args.append("-SUBSYSTEM:efi_runtime_driver");
- add_uefi_link_args(lj);
- break;
- case TargetSubsystemNative:
- lj->args.append("-SUBSYSTEM:native");
- add_win_link_args(lj, is_library, &have_windows_dll_import_libs);
- break;
- case TargetSubsystemPosix:
- lj->args.append("-SUBSYSTEM:posix");
- add_win_link_args(lj, is_library, &have_windows_dll_import_libs);
- break;
- case TargetSubsystemWindows:
- lj->args.append("-SUBSYSTEM:windows");
- add_win_link_args(lj, is_library, &have_windows_dll_import_libs);
- break;
- }
-
- // libc++ dep
- if (g->libcpp_link_lib != nullptr && g->out_type != OutTypeObj) {
- lj->args.append(build_libcxxabi(g, lj->build_dep_prog_node));
- lj->args.append(build_libcxx(g, lj->build_dep_prog_node));
- lj->args.append(build_libunwind(g, lj->build_dep_prog_node));
- }
-
- if (g->out_type == OutTypeExe || (g->out_type == OutTypeLib && g->is_dynamic)) {
- if (g->libc_link_lib == nullptr && !g->is_dummy_so) {
- Buf *libc_a_path = build_c(g, OutTypeLib, lj->build_dep_prog_node);
- lj->args.append(buf_ptr(libc_a_path));
- }
-
- // msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage
- Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib, lj->build_dep_prog_node);
- lj->args.append(buf_ptr(compiler_rt_o_path));
- }
-
- for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) {
- LinkLib *link_lib = g->link_libs_list.at(lib_i);
- if (buf_eql_str(link_lib->name, "c")) {
- continue;
- }
- if (target_is_libcpp_lib_name(g->zig_target, buf_ptr(link_lib->name))) {
- // libc++ is linked specially
- continue;
- }
- if (g->libc == nullptr && target_is_libc_lib_name(g->zig_target, buf_ptr(link_lib->name))) {
- // these libraries are always linked below when targeting glibc
- continue;
- }
- bool is_sys_lib = is_mingw_link_lib(link_lib->name);
- if (have_windows_dll_import_libs && is_sys_lib) {
- continue;
- }
- // If we're linking in the CRT or the libs are provided explictly we don't want to generate def/libs
- if ((lj->link_in_crt && is_sys_lib) || link_lib->provided_explicitly) {
- if (target_abi_is_gnu(lj->codegen->zig_target->abi)) {
- if (buf_eql_str(link_lib->name, "uuid")) {
- // mingw-w64 provides this lib
- lj->args.append(get_libc_crt_file(g, "uuid.lib", lj->build_dep_prog_node));
- } else {
- Buf* lib_name = buf_sprintf("lib%s.a", buf_ptr(link_lib->name));
- lj->args.append(buf_ptr(lib_name));
- }
- } else {
- Buf* lib_name = buf_sprintf("%s.lib", buf_ptr(link_lib->name));
- lj->args.append(buf_ptr(lib_name));
- }
- continue;
- }
-
- // This library may be a system one and we may have a suitable .lib file
-
- // Normalize the library name to lower case, the FS may be
- // case-sensitive
- char *name = strdup(buf_ptr(link_lib->name));
- assert(name != nullptr);
- for (char *ch = name; *ch; ++ch) *ch = tolower(*ch);
-
- Buf lib_path = BUF_INIT;
- err = find_mingw_lib_def(lj, name, &lib_path);
-
- if (err == ErrorFileNotFound) {
- zig_panic("link: could not find .def file to build %s\n", name);
- } else if (err != ErrorNone) {
- zig_panic("link: unable to check if .def file for %s exists: %s",
- name, err_str(err));
- }
-
- lj->args.append(get_def_lib(g, name, &lib_path));
-
- mem::os::free(name);
- }
-}
-
-static void construct_linker_job_macho(LinkJob *lj) {
- CodeGen *g = lj->codegen;
-
- lj->args.append("-error-limit");
- lj->args.append("0");
- lj->args.append("-demangle");
-
- switch (g->linker_gc_sections) {
- case OptionalBoolNull:
- // TODO why do we not follow the same logic of elf here?
- break;
- case OptionalBoolTrue:
- lj->args.append("--gc-sections");
- break;
- case OptionalBoolFalse:
- break;
- }
-
- if (g->linker_rdynamic) {
- lj->args.append("-export_dynamic");
- }
-
- if (g->linker_optimization != nullptr) {
- lj->args.append(buf_ptr(g->linker_optimization));
- }
-
- if (g->linker_z_nodelete) {
- lj->args.append("-z");
- lj->args.append("nodelete");
- }
- if (g->linker_z_defs) {
- lj->args.append("-z");
- lj->args.append("defs");
- }
-
- bool is_lib = g->out_type == OutTypeLib;
- bool is_dyn_lib = g->is_dynamic && is_lib;
- if (is_lib && !g->is_dynamic) {
- lj->args.append("-static");
- } else {
- lj->args.append("-dynamic");
- }
-
- if (is_dyn_lib) {
- lj->args.append("-dylib");
-
- Buf *compat_vers = buf_sprintf("%" ZIG_PRI_usize ".0.0", g->version_major);
- lj->args.append("-compatibility_version");
- lj->args.append(buf_ptr(compat_vers));
-
- Buf *cur_vers = buf_sprintf("%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize,
- g->version_major, g->version_minor, g->version_patch);
- lj->args.append("-current_version");
- lj->args.append(buf_ptr(cur_vers));
-
- // TODO getting an error when running an executable when doing this rpath thing
- //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib",
- // buf_ptr(g->root_out_name), g->version_major);
- //lj->args.append("-install_name");
- //lj->args.append(buf_ptr(dylib_install_name));
-
- assert(buf_len(&g->bin_file_output_path) != 0);
- }
-
- lj->args.append("-arch");
- lj->args.append(get_darwin_arch_string(g->zig_target));
-
- if (g->zig_target->glibc_or_darwin_version != nullptr) {
- if (g->zig_target->os == OsMacOSX) {
- lj->args.append("-macosx_version_min");
- } else if (g->zig_target->os == OsIOS) {
- if (g->zig_target->arch == ZigLLVM_x86 || g->zig_target->arch == ZigLLVM_x86_64) {
- lj->args.append("-ios_simulator_version_min");
- } else {
- lj->args.append("-iphoneos_version_min");
- }
- }
-
- Buf *version_string = buf_sprintf("%d.%d.%d",
- g->zig_target->glibc_or_darwin_version->major,
- g->zig_target->glibc_or_darwin_version->minor,
- g->zig_target->glibc_or_darwin_version->patch);
- lj->args.append(buf_ptr(version_string));
-
- lj->args.append("-sdk_version");
- lj->args.append(buf_ptr(version_string));
- } else if (stage2_is_zig0 && g->zig_target->os == OsMacOSX) {
- // running `zig0`; `-pie` requires versions >= 10.5; select 10.13
- lj->args.append("-macosx_version_min");
- lj->args.append("10.13");
- lj->args.append("-sdk_version");
- lj->args.append("10.13");
- }
-
- if (g->out_type == OutTypeExe) {
- lj->args.append("-pie");
- }
-
- lj->args.append("-o");
- lj->args.append(buf_ptr(&g->bin_file_output_path));
-
- for (size_t i = 0; i < g->rpath_list.length; i += 1) {
- Buf *rpath = g->rpath_list.at(i);
- add_rpath(lj, rpath);
- }
- if (is_dyn_lib) {
- add_rpath(lj, &g->bin_file_output_path);
- }
-
- if (is_dyn_lib) {
- if (g->system_linker_hack) {
- lj->args.append("-headerpad_max_install_names");
- }
- }
-
- for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
- const char *lib_dir = g->lib_dirs.at(i);
- lj->args.append("-L");
- lj->args.append(lib_dir);
- }
-
- // .o files
- for (size_t i = 0; i < g->link_objects.length; i += 1) {
- lj->args.append((const char *)buf_ptr(g->link_objects.at(i)));
- }
-
- // compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce
- if (g->out_type == OutTypeExe || is_dyn_lib) {
- Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib, lj->build_dep_prog_node);
- lj->args.append(buf_ptr(compiler_rt_o_path));
- }
-
- // libraries
- for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) {
- LinkLib *link_lib = g->link_libs_list.at(lib_i);
- if (buf_eql_str(link_lib->name, "c")) {
- // libc is linked specially
- continue;
- }
- if (target_is_libcpp_lib_name(g->zig_target, buf_ptr(link_lib->name))) {
- // libc++ is linked specially
- continue;
- }
- if (g->zig_target->is_native_os && target_is_libc_lib_name(g->zig_target, buf_ptr(link_lib->name))) {
- // libSystem is linked specially
- continue;
- }
-
- Buf *arg;
- if (buf_starts_with_str(link_lib->name, "/") || buf_ends_with_str(link_lib->name, ".a") ||
- buf_ends_with_str(link_lib->name, ".dylib"))
- {
- arg = link_lib->name;
- } else {
- arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
- }
- lj->args.append(buf_ptr(arg));
- }
-
- // libc++ dep
- if (g->libcpp_link_lib != nullptr && g->out_type != OutTypeObj) {
- lj->args.append(build_libcxxabi(g, lj->build_dep_prog_node));
- lj->args.append(build_libcxx(g, lj->build_dep_prog_node));
- }
-
- // libc dep
- if (g->zig_target->is_native_os || stage2_is_zig0) {
- // on Darwin, libSystem has libc in it, but also you have to use it
- // to make syscalls because the syscall numbers are not documented
- // and change between versions.
- // so we always link against libSystem
- lj->args.append("-lSystem");
- }
-
- for (size_t i = 0; i < g->framework_dirs.length; i += 1) {
- const char *framework_dir = g->framework_dirs.at(i);
- lj->args.append("-F");
- lj->args.append(framework_dir);
- }
-
- for (size_t i = 0; i < g->darwin_frameworks.length; i += 1) {
- lj->args.append("-framework");
- lj->args.append(buf_ptr(g->darwin_frameworks.at(i)));
- }
-
- switch (g->linker_allow_shlib_undefined) {
- case OptionalBoolNull:
- if (!g->zig_target->is_native_os && !stage2_is_zig0) {
- // TODO https://github.com/ziglang/zig/issues/5059
- lj->args.append("-undefined");
- lj->args.append("dynamic_lookup");
- }
- break;
- case OptionalBoolFalse:
- break;
- case OptionalBoolTrue:
- lj->args.append("-undefined");
- lj->args.append("dynamic_lookup");
- break;
- }
- switch (g->linker_bind_global_refs_locally) {
- case OptionalBoolNull:
- case OptionalBoolFalse:
- break;
- case OptionalBoolTrue:
- lj->args.append("-Bsymbolic");
- break;
- }
-}
-
-static void construct_linker_job(LinkJob *lj) {
- switch (target_object_format(lj->codegen->zig_target)) {
- case ZigLLVM_UnknownObjectFormat:
- case ZigLLVM_XCOFF:
- zig_unreachable();
-
- case ZigLLVM_COFF:
- return construct_linker_job_coff(lj);
- case ZigLLVM_ELF:
- return construct_linker_job_elf(lj);
- case ZigLLVM_MachO:
- return construct_linker_job_macho(lj);
- case ZigLLVM_Wasm:
- return construct_linker_job_wasm(lj);
- }
-}
-
-void zig_link_add_compiler_rt(CodeGen *g, Stage2ProgressNode *progress_node) {
- Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeObj, progress_node);
- g->link_objects.append(compiler_rt_o_path);
-}
-
-void codegen_link(CodeGen *g) {
- codegen_add_time_event(g, "Build Dependencies");
- LinkJob lj = {0};
-
- {
- const char *progress_name = "Build Dependencies";
- codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node,
- progress_name, strlen(progress_name), 0));
- lj.build_dep_prog_node = g->sub_progress_node;
- }
-
-
- // even though we're calling LLD as a library it thinks the first
- // argument is its own exe name
- lj.args.append("lld");
-
- lj.rpath_table.init(4);
- lj.codegen = g;
-
- if (g->out_type == OutTypeObj) {
- lj.args.append("-r");
- }
-
- if (g->out_type == OutTypeLib && !g->is_dynamic && !target_is_wasm(g->zig_target)) {
- ZigList file_names = {};
- for (size_t i = 0; i < g->link_objects.length; i += 1) {
- file_names.append(buf_ptr(g->link_objects.at(i)));
- }
- ZigLLVM_OSType os_type = get_llvm_os_type(g->zig_target->os);
- codegen_add_time_event(g, "LLVM Link");
- {
- const char *progress_name = "Link";
- codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node,
- progress_name, strlen(progress_name), 0));
- }
- if (g->verbose_link) {
- fprintf(stderr, "ar rcs %s", buf_ptr(&g->bin_file_output_path));
- for (size_t i = 0; i < file_names.length; i += 1) {
- fprintf(stderr, " %s", file_names.at(i));
- }
- fprintf(stderr, "\n");
- }
- if (ZigLLVMWriteArchive(buf_ptr(&g->bin_file_output_path), file_names.items, file_names.length, os_type)) {
- fprintf(stderr, "Unable to write archive '%s'\n", buf_ptr(&g->bin_file_output_path));
- exit(1);
- }
- return;
- }
-
- lj.link_in_crt = (g->libc_link_lib != nullptr && g->out_type == OutTypeExe);
-
- construct_linker_job(&lj);
-
- if (g->verbose_link) {
- for (size_t i = 0; i < lj.args.length; i += 1) {
- const char *space = (i != 0) ? " " : "";
- fprintf(stderr, "%s%s", space, lj.args.at(i));
- }
- fprintf(stderr, "\n");
- }
-
- Buf diag = BUF_INIT;
-
- codegen_add_time_event(g, "LLVM Link");
- {
- const char *progress_name = "Link";
- codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node,
- progress_name, strlen(progress_name), 0));
- }
- if (g->system_linker_hack && g->zig_target->os == OsMacOSX) {
- Termination term;
- ZigList args = {};
- args.append("ld");
- for (size_t i = 1; i < lj.args.length; i += 1) {
- args.append(lj.args.at(i));
- }
- os_spawn_process(args, &term);
- if (term.how != TerminationIdClean || term.code != 0) {
- exit(1);
- }
- } else if (!zig_lld_link(target_object_format(g->zig_target), lj.args.items, lj.args.length, &diag)) {
- fprintf(stderr, "%s\n", buf_ptr(&diag));
- exit(1);
- }
-}
-
diff --git a/src/link.zig b/src/link.zig
new file mode 100644
index 0000000000..4d28c8a1a7
--- /dev/null
+++ b/src/link.zig
@@ -0,0 +1,549 @@
+const std = @import("std");
+const mem = std.mem;
+const Allocator = std.mem.Allocator;
+const fs = std.fs;
+const log = std.log.scoped(.link);
+const assert = std.debug.assert;
+
+const Compilation = @import("Compilation.zig");
+const Module = @import("Module.zig");
+const trace = @import("tracy.zig").trace;
+const Package = @import("Package.zig");
+const Type = @import("type.zig").Type;
+const Cache = @import("Cache.zig");
+const build_options = @import("build_options");
+const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
+
+pub const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version;
+
+pub const Emit = struct {
+ /// Where the output will go.
+ directory: Compilation.Directory,
+ /// Path to the output file, relative to `directory`.
+ sub_path: []const u8,
+};
+
+pub const Options = struct {
+ /// This is `null` when -fno-emit-bin is used. When `openPath` or `flush` is called,
+ /// it will have already been null-checked.
+ emit: ?Emit,
+ target: std.Target,
+ output_mode: std.builtin.OutputMode,
+ link_mode: std.builtin.LinkMode,
+ object_format: std.builtin.ObjectFormat,
+ optimize_mode: std.builtin.Mode,
+ machine_code_model: std.builtin.CodeModel,
+ root_name: []const u8,
+ /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`.
+ module: ?*Module,
+ dynamic_linker: ?[]const u8,
+ /// Used for calculating how much space to reserve for symbols in case the binary file
+ /// does not already have a symbol table.
+ symbol_count_hint: u64 = 32,
+ /// Used for calculating how much space to reserve for executable program code in case
+ /// the binary file does not already have such a section.
+ program_code_size_hint: u64 = 256 * 1024,
+ entry_addr: ?u64 = null,
+ stack_size_override: ?u64,
+ /// Set to `true` to omit debug info.
+ strip: bool,
+ /// If this is true then this link code is responsible for outputting an object
+ /// file and then using LLD to link it together with the link options and other objects.
+ /// Otherwise (depending on `use_llvm`) this link code directly outputs and updates the final binary.
+ use_lld: bool,
+ /// If this is true then this link code is responsible for making an LLVM IR Module,
+ /// outputting it to an object file, and then linking that together with link options and
+ /// other objects.
+ /// Otherwise (depending on `use_lld`) this link code directly outputs and updates the final binary.
+ use_llvm: bool,
+ link_libc: bool,
+ link_libcpp: bool,
+ function_sections: bool,
+ eh_frame_hdr: bool,
+ rdynamic: bool,
+ z_nodelete: bool,
+ z_defs: bool,
+ bind_global_refs_locally: bool,
+ is_native_os: bool,
+ pic: bool,
+ valgrind: bool,
+ stack_check: bool,
+ single_threaded: bool,
+ verbose_link: bool,
+ dll_export_fns: bool,
+ error_return_tracing: bool,
+ is_compiler_rt_or_libc: bool,
+ parent_compilation_link_libc: bool,
+ each_lib_rpath: bool,
+ disable_lld_caching: bool,
+ is_test: bool,
+ gc_sections: ?bool = null,
+ allow_shlib_undefined: ?bool,
+ subsystem: ?std.Target.SubSystem,
+ linker_script: ?[]const u8,
+ version_script: ?[]const u8,
+ override_soname: ?[]const u8,
+ llvm_cpu_features: ?[*:0]const u8,
+ /// Extra args passed directly to LLD. Ignored when not linking with LLD.
+ extra_lld_args: []const []const u8,
+
+ objects: []const []const u8,
+ framework_dirs: []const []const u8,
+ frameworks: []const []const u8,
+ system_libs: std.StringArrayHashMapUnmanaged(void),
+ lib_dirs: []const []const u8,
+ rpath_list: []const []const u8,
+
+ version: ?std.builtin.Version,
+ libc_installation: ?*const LibCInstallation,
+
+ pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
+ return if (options.use_lld) .Obj else options.output_mode;
+ }
+};
+
+pub const File = struct {
+ tag: Tag,
+ options: Options,
+ file: ?fs.File,
+ allocator: *Allocator,
+ /// When linking with LLD, this linker code will output an object file only at
+ /// this location, and then this path can be placed on the LLD linker line.
+ intermediary_basename: ?[]const u8 = null,
+
+ /// Prevents other processes from clobbering files in the output directory
+ /// of this linking operation.
+ lock: ?Cache.Lock = null,
+
+ pub const LinkBlock = union {
+ elf: Elf.TextBlock,
+ coff: Coff.TextBlock,
+ macho: MachO.TextBlock,
+ c: void,
+ wasm: void,
+ };
+
+ pub const LinkFn = union {
+ elf: Elf.SrcFn,
+ coff: Coff.SrcFn,
+ macho: MachO.SrcFn,
+ c: void,
+ wasm: ?Wasm.FnData,
+ };
+
+ /// For DWARF .debug_info.
+ pub const DbgInfoTypeRelocsTable = std.HashMapUnmanaged(Type, DbgInfoTypeReloc, Type.hash, Type.eql, std.hash_map.DefaultMaxLoadPercentage);
+
+ /// For DWARF .debug_info.
+ pub const DbgInfoTypeReloc = struct {
+ /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl).
+ /// This is where the .debug_info tag for the type is.
+ off: u32,
+ /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl).
+ /// List of DW.AT_type / DW.FORM_ref4 that points to the type.
+ relocs: std.ArrayListUnmanaged(u32),
+ };
+
+ /// Attempts incremental linking, if the file already exists. If
+ /// incremental linking fails, falls back to truncating the file and
+ /// rewriting it. A malicious file is detected as incremental link failure
+ /// and does not cause Illegal Behavior. This operation is not atomic.
+ pub fn openPath(allocator: *Allocator, options: Options) !*File {
+ const use_stage1 = build_options.is_stage1 and options.use_llvm;
+ if (use_stage1 or options.emit == null) {
+ return switch (options.object_format) {
+ .coff, .pe => &(try Coff.createEmpty(allocator, options)).base,
+ .elf => &(try Elf.createEmpty(allocator, options)).base,
+ .macho => &(try MachO.createEmpty(allocator, options)).base,
+ .wasm => &(try Wasm.createEmpty(allocator, options)).base,
+ .c => unreachable, // Reported error earlier.
+ .hex => return error.HexObjectFormatUnimplemented,
+ .raw => return error.RawObjectFormatUnimplemented,
+ };
+ }
+ const emit = options.emit.?;
+ const use_lld = build_options.have_llvm and options.use_lld; // comptime known false when !have_llvm
+ const sub_path = if (use_lld) blk: {
+ if (options.module == null) {
+ // No point in opening a file, we would not write anything to it. Initialize with empty.
+ return switch (options.object_format) {
+ .coff, .pe => &(try Coff.createEmpty(allocator, options)).base,
+ .elf => &(try Elf.createEmpty(allocator, options)).base,
+ .macho => &(try MachO.createEmpty(allocator, options)).base,
+ .wasm => &(try Wasm.createEmpty(allocator, options)).base,
+ .c => unreachable, // Reported error earlier.
+ .hex => return error.HexObjectFormatUnimplemented,
+ .raw => return error.RawObjectFormatUnimplemented,
+ };
+ }
+ // Open a temporary object file, not the final output file because we want to link with LLD.
+ break :blk try std.fmt.allocPrint(allocator, "{s}{s}", .{ emit.sub_path, options.target.oFileExt() });
+ } else emit.sub_path;
+ errdefer if (use_lld) allocator.free(sub_path);
+
+ const file: *File = switch (options.object_format) {
+ .coff, .pe => &(try Coff.openPath(allocator, sub_path, options)).base,
+ .elf => &(try Elf.openPath(allocator, sub_path, options)).base,
+ .macho => &(try MachO.openPath(allocator, sub_path, options)).base,
+ .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base,
+ .c => &(try C.openPath(allocator, sub_path, options)).base,
+ .hex => return error.HexObjectFormatUnimplemented,
+ .raw => return error.RawObjectFormatUnimplemented,
+ };
+
+ if (use_lld) {
+ file.intermediary_basename = sub_path;
+ }
+
+ return file;
+ }
+
+ pub fn cast(base: *File, comptime T: type) ?*T {
+ if (base.tag != T.base_tag)
+ return null;
+
+ return @fieldParentPtr(T, "base", base);
+ }
+
+ pub fn makeWritable(base: *File) !void {
+ switch (base.tag) {
+ .coff, .elf, .macho => {
+ if (base.file != null) return;
+ const emit = base.options.emit orelse return;
+ base.file = try emit.directory.handle.createFile(emit.sub_path, .{
+ .truncate = false,
+ .read = true,
+ .mode = determineMode(base.options),
+ });
+ },
+ .c, .wasm => {},
+ }
+ }
+
+ pub fn makeExecutable(base: *File) !void {
+ switch (base.tag) {
+ .coff, .elf, .macho => if (base.file) |f| {
+ if (base.intermediary_basename != null) {
+ // The file we have open is not the final file that we want to
+ // make executable, so we don't have to close it.
+ return;
+ }
+ f.close();
+ base.file = null;
+ },
+ .c, .wasm => {},
+ }
+ }
+
+ /// May be called before or after updateDeclExports but must be called
+ /// after allocateDeclIndexes for any given Decl.
+ pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
+ switch (base.tag) {
+ .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl),
+ .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
+ .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),
+ .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
+ .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl),
+ }
+ }
+
+ pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void {
+ switch (base.tag) {
+ .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),
+ .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
+ .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
+ .c, .wasm => {},
+ }
+ }
+
+ /// Must be called before any call to updateDecl or updateDeclExports for
+ /// any given Decl.
+ pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
+ switch (base.tag) {
+ .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl),
+ .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
+ .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),
+ .c, .wasm => {},
+ }
+ }
+
+ pub fn releaseLock(self: *File) void {
+ if (self.lock) |*lock| {
+ lock.release();
+ self.lock = null;
+ }
+ }
+
+ pub fn toOwnedLock(self: *File) Cache.Lock {
+ const lock = self.lock.?;
+ self.lock = null;
+ return lock;
+ }
+
+ pub fn destroy(base: *File) void {
+ base.releaseLock();
+ if (base.file) |f| f.close();
+ if (base.intermediary_basename) |sub_path| base.allocator.free(sub_path);
+ switch (base.tag) {
+ .coff => {
+ const parent = @fieldParentPtr(Coff, "base", base);
+ parent.deinit();
+ base.allocator.destroy(parent);
+ },
+ .elf => {
+ const parent = @fieldParentPtr(Elf, "base", base);
+ parent.deinit();
+ base.allocator.destroy(parent);
+ },
+ .macho => {
+ const parent = @fieldParentPtr(MachO, "base", base);
+ parent.deinit();
+ base.allocator.destroy(parent);
+ },
+ .c => {
+ const parent = @fieldParentPtr(C, "base", base);
+ parent.deinit();
+ base.allocator.destroy(parent);
+ },
+ .wasm => {
+ const parent = @fieldParentPtr(Wasm, "base", base);
+ parent.deinit();
+ base.allocator.destroy(parent);
+ },
+ }
+ }
+
+ /// Commit pending changes and write headers. Takes into account final output mode
+ /// and `use_lld`, not only `effectiveOutputMode`.
+ pub fn flush(base: *File, comp: *Compilation) !void {
+ const emit = base.options.emit orelse return; // -fno-emit-bin
+
+ if (comp.clang_preprocessor_mode == .yes) {
+ // TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case)
+ // Until then, we do `lld -r -o output.o input.o` even though the output is the same
+ // as the input. For the preprocessing case (`zig cc -E -o foo`) we copy the file
+ // to the final location. See also the corresponding TODO in Coff linking.
+ const full_out_path = try emit.directory.join(comp.gpa, &[_][]const u8{emit.sub_path});
+ defer comp.gpa.free(full_out_path);
+ assert(comp.c_object_table.count() == 1);
+ const the_entry = comp.c_object_table.items()[0];
+ const cached_pp_file_path = the_entry.key.status.success.object_path;
+ try fs.cwd().copyFile(cached_pp_file_path, fs.cwd(), full_out_path, .{});
+ return;
+ }
+ const use_lld = build_options.have_llvm and base.options.use_lld;
+ if (use_lld and base.options.output_mode == .Lib and base.options.link_mode == .Static and
+ !base.options.target.isWasm())
+ {
+ return base.linkAsArchive(comp);
+ }
+ switch (base.tag) {
+ .coff => return @fieldParentPtr(Coff, "base", base).flush(comp),
+ .elf => return @fieldParentPtr(Elf, "base", base).flush(comp),
+ .macho => return @fieldParentPtr(MachO, "base", base).flush(comp),
+ .c => return @fieldParentPtr(C, "base", base).flush(comp),
+ .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp),
+ }
+ }
+
+ /// Commit pending changes and write headers. Works based on `effectiveOutputMode`
+ /// rather than final output mode.
+ pub fn flushModule(base: *File, comp: *Compilation) !void {
+ switch (base.tag) {
+ .coff => return @fieldParentPtr(Coff, "base", base).flushModule(comp),
+ .elf => return @fieldParentPtr(Elf, "base", base).flushModule(comp),
+ .macho => return @fieldParentPtr(MachO, "base", base).flushModule(comp),
+ .c => return @fieldParentPtr(C, "base", base).flushModule(comp),
+ .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp),
+ }
+ }
+
+ pub fn freeDecl(base: *File, decl: *Module.Decl) void {
+ switch (base.tag) {
+ .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl),
+ .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
+ .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),
+ .c => unreachable,
+ .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl),
+ }
+ }
+
+ pub fn errorFlags(base: *File) ErrorFlags {
+ switch (base.tag) {
+ .coff => return @fieldParentPtr(Coff, "base", base).error_flags,
+ .elf => return @fieldParentPtr(Elf, "base", base).error_flags,
+ .macho => return @fieldParentPtr(MachO, "base", base).error_flags,
+ .c => return .{ .no_entry_point_found = false },
+ .wasm => return ErrorFlags{},
+ }
+ }
+
+ /// May be called before or after updateDecl, but must be called after
+ /// allocateDeclIndexes for any given Decl.
+ pub fn updateDeclExports(
+ base: *File,
+ module: *Module,
+ decl: *const Module.Decl,
+ exports: []const *Module.Export,
+ ) !void {
+ switch (base.tag) {
+ .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports),
+ .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
+ .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),
+ .c => return {},
+ .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports),
+ }
+ }
+
+ pub fn getDeclVAddr(base: *File, decl: *const Module.Decl) u64 {
+ switch (base.tag) {
+ .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl),
+ .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl),
+ .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl),
+ .c => unreachable,
+ .wasm => unreachable,
+ }
+ }
+
+ fn linkAsArchive(base: *File, comp: *Compilation) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ var arena_allocator = std.heap.ArenaAllocator.init(base.allocator);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ const directory = base.options.emit.?.directory; // Just an alias to make it shorter to type.
+
+ // If there is no Zig code to compile, then we should skip flushing the output file because it
+ // will not be part of the linker line anyway.
+ const module_obj_path: ?[]const u8 = if (base.options.module) |module| blk: {
+ const use_stage1 = build_options.is_stage1 and base.options.use_llvm;
+ if (use_stage1) {
+ const obj_basename = try std.zig.binNameAlloc(arena, .{
+ .root_name = base.options.root_name,
+ .target = base.options.target,
+ .output_mode = .Obj,
+ });
+ const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename});
+ break :blk full_obj_path;
+ }
+ try base.flushModule(comp);
+ const obj_basename = base.intermediary_basename.?;
+ const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename});
+ break :blk full_obj_path;
+ } else null;
+
+ // This function follows the same pattern as link.Elf.linkWithLLD so if you want some
+ // insight as to what's going on here you can read that function body which is more
+ // well-commented.
+
+ const id_symlink_basename = "llvm-ar.id";
+
+ base.releaseLock();
+
+ var ch = comp.cache_parent.obtain();
+ defer ch.deinit();
+
+ try ch.addListOfFiles(base.options.objects);
+ for (comp.c_object_table.items()) |entry| {
+ _ = try ch.addFile(entry.key.status.success.object_path, null);
+ }
+ try ch.addOptionalFile(module_obj_path);
+
+ // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
+ _ = try ch.hit();
+ const digest = ch.final();
+
+ var prev_digest_buf: [digest.len]u8 = undefined;
+ const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| b: {
+ log.debug("archive new_digest={} readlink error: {}", .{ digest, @errorName(err) });
+ break :b prev_digest_buf[0..0];
+ };
+ if (mem.eql(u8, prev_digest, &digest)) {
+ log.debug("archive digest={} match - skipping invocation", .{digest});
+ base.lock = ch.toOwnedLock();
+ return;
+ }
+
+ // We are about to change the output file to be different, so we invalidate the build hash now.
+ directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
+ error.FileNotFound => {},
+ else => |e| return e,
+ };
+
+ var object_files = std.ArrayList([*:0]const u8).init(base.allocator);
+ defer object_files.deinit();
+
+ try object_files.ensureCapacity(base.options.objects.len + comp.c_object_table.items().len + 1);
+ for (base.options.objects) |obj_path| {
+ object_files.appendAssumeCapacity(try arena.dupeZ(u8, obj_path));
+ }
+ for (comp.c_object_table.items()) |entry| {
+ object_files.appendAssumeCapacity(try arena.dupeZ(u8, entry.key.status.success.object_path));
+ }
+ if (module_obj_path) |p| {
+ object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));
+ }
+
+ const full_out_path = try directory.join(arena, &[_][]const u8{base.options.emit.?.sub_path});
+ const full_out_path_z = try arena.dupeZ(u8, full_out_path);
+
+ if (base.options.verbose_link) {
+ std.debug.print("ar rcs {}", .{full_out_path_z});
+ for (object_files.items) |arg| {
+ std.debug.print(" {}", .{arg});
+ }
+ std.debug.print("\n", .{});
+ }
+
+ const llvm = @import("llvm.zig");
+ const os_type = @import("target.zig").osToLLVM(base.options.target.os.tag);
+ const bad = llvm.WriteArchive(full_out_path_z, object_files.items.ptr, object_files.items.len, os_type);
+ if (bad) return error.UnableToWriteArchive;
+
+ directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
+ std.log.warn("failed to save archive hash digest symlink: {}", .{@errorName(err)});
+ };
+
+ ch.writeManifest() catch |err| {
+ std.log.warn("failed to write cache manifest when archiving: {}", .{@errorName(err)});
+ };
+
+ base.lock = ch.toOwnedLock();
+ }
+
+ pub const Tag = enum {
+ coff,
+ elf,
+ macho,
+ c,
+ wasm,
+ };
+
+ pub const ErrorFlags = struct {
+ no_entry_point_found: bool = false,
+ };
+
+ pub const C = @import("link/C.zig");
+ pub const Coff = @import("link/Coff.zig");
+ pub const Elf = @import("link/Elf.zig");
+ pub const MachO = @import("link/MachO.zig");
+ pub const Wasm = @import("link/Wasm.zig");
+};
+
+pub fn determineMode(options: Options) fs.File.Mode {
+ // On common systems with a 0o022 umask, 0o777 will still result in a file created
+ // with 0o755 permissions, but it works appropriately if the system is configured
+ // more leniently. As another data point, C's fopen seems to open files with the
+ // 666 mode.
+ const executable_mode = if (std.Target.current.os.tag == .windows) 0 else 0o777;
+ switch (options.effectiveOutputMode()) {
+ .Lib => return switch (options.link_mode) {
+ .Dynamic => executable_mode,
+ .Static => fs.File.default_mode,
+ },
+ .Exe => return executable_mode,
+ .Obj => return fs.File.default_mode,
+ }
+}
diff --git a/src-self-hosted/link/C.zig b/src/link/C.zig
similarity index 75%
rename from src-self-hosted/link/C.zig
rename to src/link/C.zig
index 69eabd1f8b..467e10998e 100644
--- a/src-self-hosted/link/C.zig
+++ b/src/link/C.zig
@@ -3,9 +3,11 @@ const mem = std.mem;
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const Module = @import("../Module.zig");
+const Compilation = @import("../Compilation.zig");
const fs = std.fs;
const codegen = @import("../codegen/c.zig");
const link = @import("../link.zig");
+const trace = @import("../tracy.zig").trace;
const File = link.File;
const C = @This();
@@ -20,12 +22,15 @@ main: std.ArrayList(u8),
called: std.StringHashMap(void),
need_stddef: bool = false,
need_stdint: bool = false,
-error_msg: *Module.ErrorMsg = undefined,
+error_msg: *Compilation.ErrorMsg = undefined,
-pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File {
+pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*C {
assert(options.object_format == .c);
- const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.determineMode(options) });
+ if (options.use_llvm) return error.LLVMHasNoCBackend;
+ if (options.use_lld) return error.LLDHasNoCBackend;
+
+ const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.determineMode(options) });
errdefer file.close();
var c_file = try allocator.create(C);
@@ -44,11 +49,11 @@ pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, option
.called = std.StringHashMap(void).init(allocator),
};
- return &c_file.base;
+ return c_file;
}
pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
- self.error_msg = try Module.ErrorMsg.create(self.base.allocator, src, format, args);
+ self.error_msg = try Compilation.ErrorMsg.create(self.base.allocator, src, format, args);
return error.AnalysisFail;
}
@@ -68,7 +73,14 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
};
}
-pub fn flush(self: *C, module: *Module) !void {
+pub fn flush(self: *C, comp: *Compilation) !void {
+ return self.flushModule(comp);
+}
+
+pub fn flushModule(self: *C, comp: *Compilation) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
const writer = self.base.file.?.writer();
try writer.writeAll(@embedFile("cbe.h"));
var includes = false;
diff --git a/src/link/Coff.zig b/src/link/Coff.zig
new file mode 100644
index 0000000000..3f462582e7
--- /dev/null
+++ b/src/link/Coff.zig
@@ -0,0 +1,1220 @@
+const Coff = @This();
+
+const std = @import("std");
+const log = std.log.scoped(.link);
+const Allocator = std.mem.Allocator;
+const assert = std.debug.assert;
+const fs = std.fs;
+const allocPrint = std.fmt.allocPrint;
+const mem = std.mem;
+
+const trace = @import("../tracy.zig").trace;
+const Module = @import("../Module.zig");
+const Compilation = @import("../Compilation.zig");
+const codegen = @import("../codegen.zig");
+const link = @import("../link.zig");
+const build_options = @import("build_options");
+const Cache = @import("../Cache.zig");
+const mingw = @import("../mingw.zig");
+
+const allocation_padding = 4 / 3;
+const minimum_text_block_size = 64 * allocation_padding;
+
+const section_alignment = 4096;
+const file_alignment = 512;
+const image_base = 0x400_000;
+const section_table_size = 2 * 40;
+comptime {
+ assert(mem.isAligned(image_base, section_alignment));
+}
+
+pub const base_tag: link.File.Tag = .coff;
+
+const msdos_stub = @embedFile("msdos-stub.bin");
+
+base: link.File,
+ptr_width: PtrWidth,
+error_flags: link.File.ErrorFlags = .{},
+
+text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
+last_text_block: ?*TextBlock = null,
+
+/// Section table file pointer.
+section_table_offset: u32 = 0,
+/// Section data file pointer.
+section_data_offset: u32 = 0,
+/// Optiona header file pointer.
+optional_header_offset: u32 = 0,
+
+/// Absolute virtual address of the offset table when the executable is loaded in memory.
+offset_table_virtual_address: u32 = 0,
+/// Current size of the offset table on disk, must be a multiple of `file_alignment`
+offset_table_size: u32 = 0,
+/// Contains absolute virtual addresses
+offset_table: std.ArrayListUnmanaged(u64) = .{},
+/// Free list of offset table indices
+offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
+
+/// Virtual address of the entry point procedure relative to `image_base`
+entry_addr: ?u32 = null,
+
+/// Absolute virtual address of the text section when the executable is loaded in memory.
+text_section_virtual_address: u32 = 0,
+/// Current size of the `.text` section on disk, must be a multiple of `file_alignment`
+text_section_size: u32 = 0,
+
+offset_table_size_dirty: bool = false,
+text_section_size_dirty: bool = false,
+/// This flag is set when the virtual size of the whole image file when loaded in memory has changed
+/// and needs to be updated in the optional header.
+size_of_image_dirty: bool = false,
+
+pub const PtrWidth = enum { p32, p64 };
+
+pub const TextBlock = struct {
+ /// Offset of the code relative to the start of the text section
+ text_offset: u32,
+ /// Used size of the text block
+ size: u32,
+ /// This field is undefined for symbols with size = 0.
+ offset_table_index: u32,
+ /// Points to the previous and next neighbors, based on the `text_offset`.
+ /// This can be used to find, for example, the capacity of this `TextBlock`.
+ prev: ?*TextBlock,
+ next: ?*TextBlock,
+
+ pub const empty = TextBlock{
+ .text_offset = 0,
+ .size = 0,
+ .offset_table_index = undefined,
+ .prev = null,
+ .next = null,
+ };
+
+ /// Returns how much room there is to grow in virtual address space.
+ fn capacity(self: TextBlock) u64 {
+ if (self.next) |next| {
+ return next.text_offset - self.text_offset;
+ }
+ // This is the last block, the capacity is only limited by the address space.
+ return std.math.maxInt(u32) - self.text_offset;
+ }
+
+ fn freeListEligible(self: TextBlock) bool {
+ // No need to keep a free list node for the last block.
+ const next = self.next orelse return false;
+ const cap = next.text_offset - self.text_offset;
+ const ideal_cap = self.size * allocation_padding;
+ if (cap <= ideal_cap) return false;
+ const surplus = cap - ideal_cap;
+ return surplus >= minimum_text_block_size;
+ }
+
+ /// Absolute virtual address of the text block when the file is loaded in memory.
+ fn getVAddr(self: TextBlock, coff: Coff) u32 {
+ return coff.text_section_virtual_address + self.text_offset;
+ }
+};
+
+pub const SrcFn = void;
+
+pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Coff {
+ assert(options.object_format == .coff);
+
+ if (options.use_llvm) return error.LLVM_BackendIsTODO_ForCoff; // TODO
+ if (options.use_lld) return error.LLD_LinkingIsTODO_ForCoff; // TODO
+
+ const file = try options.emit.?.directory.handle.createFile(sub_path, .{
+ .truncate = false,
+ .read = true,
+ .mode = link.determineMode(options),
+ });
+ errdefer file.close();
+
+ const self = try createEmpty(allocator, options);
+ errdefer self.base.destroy();
+
+ self.base.file = file;
+
+ // TODO Write object specific relocations, COFF symbol table, then enable object file output.
+ switch (options.output_mode) {
+ .Exe => {},
+ .Obj => return error.TODOImplementWritingObjFiles,
+ .Lib => return error.TODOImplementWritingLibFiles,
+ }
+
+ var coff_file_header_offset: u32 = 0;
+ if (options.output_mode == .Exe) {
+ // Write the MS-DOS stub and the PE signature
+ try self.base.file.?.pwriteAll(msdos_stub ++ "PE\x00\x00", 0);
+ coff_file_header_offset = msdos_stub.len + 4;
+ }
+
+ // COFF file header
+ const data_directory_count = 0;
+ var hdr_data: [112 + data_directory_count * 8 + section_table_size]u8 = undefined;
+ var index: usize = 0;
+
+ const machine = self.base.options.target.cpu.arch.toCoffMachine();
+ if (machine == .Unknown) {
+ return error.UnsupportedCOFFArchitecture;
+ }
+ mem.writeIntLittle(u16, hdr_data[0..2], @enumToInt(machine));
+ index += 2;
+
+ // Number of sections (we only use .got, .text)
+ mem.writeIntLittle(u16, hdr_data[index..][0..2], 2);
+ index += 2;
+ // TimeDateStamp (u32), PointerToSymbolTable (u32), NumberOfSymbols (u32)
+ mem.set(u8, hdr_data[index..][0..12], 0);
+ index += 12;
+
+ const optional_header_size = switch (options.output_mode) {
+ .Exe => data_directory_count * 8 + switch (self.ptr_width) {
+ .p32 => @as(u16, 96),
+ .p64 => 112,
+ },
+ else => 0,
+ };
+
+ const section_table_offset = coff_file_header_offset + 20 + optional_header_size;
+ const default_offset_table_size = file_alignment;
+ const default_size_of_code = 0;
+
+ self.section_data_offset = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment);
+ const section_data_relative_virtual_address = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment);
+ self.offset_table_virtual_address = image_base + section_data_relative_virtual_address;
+ self.offset_table_size = default_offset_table_size;
+ self.section_table_offset = section_table_offset;
+ self.text_section_virtual_address = image_base + section_data_relative_virtual_address + section_alignment;
+ self.text_section_size = default_size_of_code;
+
+ // Size of file when loaded in memory
+ const size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + default_size_of_code, section_alignment);
+
+ mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size);
+ index += 2;
+
+ // Characteristics
+ var characteristics: u16 = std.coff.IMAGE_FILE_DEBUG_STRIPPED | std.coff.IMAGE_FILE_RELOCS_STRIPPED; // TODO Remove debug info stripped flag when necessary
+ if (options.output_mode == .Exe) {
+ characteristics |= std.coff.IMAGE_FILE_EXECUTABLE_IMAGE;
+ }
+ switch (self.ptr_width) {
+ .p32 => characteristics |= std.coff.IMAGE_FILE_32BIT_MACHINE,
+ .p64 => characteristics |= std.coff.IMAGE_FILE_LARGE_ADDRESS_AWARE,
+ }
+ mem.writeIntLittle(u16, hdr_data[index..][0..2], characteristics);
+ index += 2;
+
+ assert(index == 20);
+ try self.base.file.?.pwriteAll(hdr_data[0..index], coff_file_header_offset);
+
+ if (options.output_mode == .Exe) {
+ self.optional_header_offset = coff_file_header_offset + 20;
+ // Optional header
+ index = 0;
+ mem.writeIntLittle(u16, hdr_data[0..2], switch (self.ptr_width) {
+ .p32 => @as(u16, 0x10b),
+ .p64 => 0x20b,
+ });
+ index += 2;
+
+ // Linker version (u8 + u8)
+ mem.set(u8, hdr_data[index..][0..2], 0);
+ index += 2;
+
+ // SizeOfCode (UNUSED, u32), SizeOfInitializedData (u32), SizeOfUninitializedData (u32), AddressOfEntryPoint (u32), BaseOfCode (UNUSED, u32)
+ mem.set(u8, hdr_data[index..][0..20], 0);
+ index += 20;
+
+ if (self.ptr_width == .p32) {
+ // Base of data relative to the image base (UNUSED)
+ mem.set(u8, hdr_data[index..][0..4], 0);
+ index += 4;
+
+ // Image base address
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], image_base);
+ index += 4;
+ } else {
+ // Image base address
+ mem.writeIntLittle(u64, hdr_data[index..][0..8], image_base);
+ index += 8;
+ }
+
+ // Section alignment
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], section_alignment);
+ index += 4;
+ // File alignment
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], file_alignment);
+ index += 4;
+ // Required OS version, 6.0 is vista
+ mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
+ index += 2;
+ mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
+ index += 2;
+ // Image version
+ mem.set(u8, hdr_data[index..][0..4], 0);
+ index += 4;
+ // Required subsystem version, same as OS version
+ mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
+ index += 2;
+ mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
+ index += 2;
+ // Reserved zeroes (u32)
+ mem.set(u8, hdr_data[index..][0..4], 0);
+ index += 4;
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], size_of_image);
+ index += 4;
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
+ index += 4;
+ // CheckSum (u32)
+ mem.set(u8, hdr_data[index..][0..4], 0);
+ index += 4;
+ // Subsystem, TODO: Let users specify the subsystem, always CUI for now
+ mem.writeIntLittle(u16, hdr_data[index..][0..2], 3);
+ index += 2;
+ // DLL characteristics
+ mem.writeIntLittle(u16, hdr_data[index..][0..2], 0x0);
+ index += 2;
+
+ switch (self.ptr_width) {
+ .p32 => {
+ // Size of stack reserve + commit
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000_000);
+ index += 4;
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
+ index += 4;
+ // Size of heap reserve + commit
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x100_000);
+ index += 4;
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
+ index += 4;
+ },
+ .p64 => {
+ // Size of stack reserve + commit
+ mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000_000);
+ index += 8;
+ mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
+ index += 8;
+ // Size of heap reserve + commit
+ mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x100_000);
+ index += 8;
+ mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
+ index += 8;
+ },
+ }
+
+ // Reserved zeroes
+ mem.set(u8, hdr_data[index..][0..4], 0);
+ index += 4;
+
+ // Number of data directories
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], data_directory_count);
+ index += 4;
+ // Initialize data directories to zero
+ mem.set(u8, hdr_data[index..][0 .. data_directory_count * 8], 0);
+ index += data_directory_count * 8;
+
+ assert(index == optional_header_size);
+ }
+
+ // Write section table.
+ // First, the .got section
+ hdr_data[index..][0..8].* = ".got\x00\x00\x00\x00".*;
+ index += 8;
+ if (options.output_mode == .Exe) {
+ // Virtual size (u32)
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
+ index += 4;
+ // Virtual address (u32)
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - image_base);
+ index += 4;
+ } else {
+ mem.set(u8, hdr_data[index..][0..8], 0);
+ index += 8;
+ }
+ // Size of raw data (u32)
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
+ index += 4;
+ // File pointer to the start of the section
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
+ index += 4;
+ // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16)
+ mem.set(u8, hdr_data[index..][0..12], 0);
+ index += 12;
+ // Section flags
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], std.coff.IMAGE_SCN_CNT_INITIALIZED_DATA | std.coff.IMAGE_SCN_MEM_READ);
+ index += 4;
+ // Then, the .text section
+ hdr_data[index..][0..8].* = ".text\x00\x00\x00".*;
+ index += 8;
+ if (options.output_mode == .Exe) {
+ // Virtual size (u32)
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
+ index += 4;
+ // Virtual address (u32)
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - image_base);
+ index += 4;
+ } else {
+ mem.set(u8, hdr_data[index..][0..8], 0);
+ index += 8;
+ }
+ // Size of raw data (u32)
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
+ index += 4;
+ // File pointer to the start of the section
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset + default_offset_table_size);
+ index += 4;
+ // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16)
+ mem.set(u8, hdr_data[index..][0..12], 0);
+ index += 12;
+ // Section flags
+ mem.writeIntLittle(
+ u32,
+ hdr_data[index..][0..4],
+ std.coff.IMAGE_SCN_CNT_CODE | std.coff.IMAGE_SCN_MEM_EXECUTE | std.coff.IMAGE_SCN_MEM_READ | std.coff.IMAGE_SCN_MEM_WRITE,
+ );
+ index += 4;
+
+ assert(index == optional_header_size + section_table_size);
+ try self.base.file.?.pwriteAll(hdr_data[0..index], self.optional_header_offset);
+ try self.base.file.?.setEndPos(self.section_data_offset + default_offset_table_size + default_size_of_code);
+
+ return self;
+}
+
+pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Coff {
+ const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) {
+ 0...32 => .p32,
+ 33...64 => .p64,
+ else => return error.UnsupportedCOFFArchitecture,
+ };
+ const self = try gpa.create(Coff);
+ self.* = .{
+ .base = .{
+ .tag = .coff,
+ .options = options,
+ .allocator = gpa,
+ .file = null,
+ },
+ .ptr_width = ptr_width,
+ };
+ return self;
+}
+
+pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void {
+ try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
+
+ if (self.offset_table_free_list.popOrNull()) |i| {
+ decl.link.coff.offset_table_index = i;
+ } else {
+ decl.link.coff.offset_table_index = @intCast(u32, self.offset_table.items.len);
+ _ = self.offset_table.addOneAssumeCapacity();
+
+ const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8;
+ if (self.offset_table.items.len > self.offset_table_size / entry_size) {
+ self.offset_table_size_dirty = true;
+ }
+ }
+
+ self.offset_table.items[decl.link.coff.offset_table_index] = 0;
+}
+
+fn allocateTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
+ const new_block_min_capacity = new_block_size * allocation_padding;
+
+ // We use these to indicate our intention to update metadata, placing the new block,
+ // and possibly removing a free list node.
+ // It would be simpler to do it inside the for loop below, but that would cause a
+ // problem if an error was returned later in the function. So this action
+ // is actually carried out at the end of the function, when errors are no longer possible.
+ var block_placement: ?*TextBlock = null;
+ var free_list_removal: ?usize = null;
+
+ const vaddr = blk: {
+ var i: usize = 0;
+ while (i < self.text_block_free_list.items.len) {
+ const free_block = self.text_block_free_list.items[i];
+
+ const next_block_text_offset = free_block.text_offset + free_block.capacity();
+ const new_block_text_offset = mem.alignForwardGeneric(u64, free_block.getVAddr(self.*) + free_block.size, alignment) - self.text_section_virtual_address;
+ if (new_block_text_offset < next_block_text_offset and next_block_text_offset - new_block_text_offset >= new_block_min_capacity) {
+ block_placement = free_block;
+
+ const remaining_capacity = next_block_text_offset - new_block_text_offset - new_block_min_capacity;
+ if (remaining_capacity < minimum_text_block_size) {
+ free_list_removal = i;
+ }
+
+ break :blk new_block_text_offset + self.text_section_virtual_address;
+ } else {
+ if (!free_block.freeListEligible()) {
+ _ = self.text_block_free_list.swapRemove(i);
+ } else {
+ i += 1;
+ }
+ continue;
+ }
+ } else if (self.last_text_block) |last| {
+ const new_block_vaddr = mem.alignForwardGeneric(u64, last.getVAddr(self.*) + last.size, alignment);
+ block_placement = last;
+ break :blk new_block_vaddr;
+ } else {
+ break :blk self.text_section_virtual_address;
+ }
+ };
+
+ const expand_text_section = block_placement == null or block_placement.?.next == null;
+ if (expand_text_section) {
+ const needed_size = @intCast(u32, mem.alignForwardGeneric(u64, vaddr + new_block_size - self.text_section_virtual_address, file_alignment));
+ if (needed_size > self.text_section_size) {
+ const current_text_section_virtual_size = mem.alignForwardGeneric(u32, self.text_section_size, section_alignment);
+ const new_text_section_virtual_size = mem.alignForwardGeneric(u32, needed_size, section_alignment);
+ if (current_text_section_virtual_size != new_text_section_virtual_size) {
+ self.size_of_image_dirty = true;
+ // Write new virtual size
+ var buf: [4]u8 = undefined;
+ mem.writeIntLittle(u32, &buf, new_text_section_virtual_size);
+ try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 8);
+ }
+
+ self.text_section_size = needed_size;
+ self.text_section_size_dirty = true;
+ }
+ self.last_text_block = text_block;
+ }
+ text_block.text_offset = @intCast(u32, vaddr - self.text_section_virtual_address);
+ text_block.size = @intCast(u32, new_block_size);
+
+ // This function can also reallocate a text block.
+ // In this case we need to "unplug" it from its previous location before
+ // plugging it in to its new location.
+ if (text_block.prev) |prev| {
+ prev.next = text_block.next;
+ }
+ if (text_block.next) |next| {
+ next.prev = text_block.prev;
+ }
+
+ if (block_placement) |big_block| {
+ text_block.prev = big_block;
+ text_block.next = big_block.next;
+ big_block.next = text_block;
+ } else {
+ text_block.prev = null;
+ text_block.next = null;
+ }
+ if (free_list_removal) |i| {
+ _ = self.text_block_free_list.swapRemove(i);
+ }
+ return vaddr;
+}
+
+fn growTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
+ const block_vaddr = text_block.getVAddr(self.*);
+ const align_ok = mem.alignBackwardGeneric(u64, block_vaddr, alignment) == block_vaddr;
+ const need_realloc = !align_ok or new_block_size > text_block.capacity();
+ if (!need_realloc) return @as(u64, block_vaddr);
+ return self.allocateTextBlock(text_block, new_block_size, alignment);
+}
+
+fn shrinkTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64) void {
+ text_block.size = @intCast(u32, new_block_size);
+ if (text_block.capacity() - text_block.size >= minimum_text_block_size) {
+ self.text_block_free_list.append(self.base.allocator, text_block) catch {};
+ }
+}
+
+fn freeTextBlock(self: *Coff, text_block: *TextBlock) void {
+ var already_have_free_list_node = false;
+ {
+ var i: usize = 0;
+ // TODO turn text_block_free_list into a hash map
+ while (i < self.text_block_free_list.items.len) {
+ if (self.text_block_free_list.items[i] == text_block) {
+ _ = self.text_block_free_list.swapRemove(i);
+ continue;
+ }
+ if (self.text_block_free_list.items[i] == text_block.prev) {
+ already_have_free_list_node = true;
+ }
+ i += 1;
+ }
+ }
+ if (self.last_text_block == text_block) {
+ self.last_text_block = text_block.prev;
+ }
+ if (text_block.prev) |prev| {
+ prev.next = text_block.next;
+
+ if (!already_have_free_list_node and prev.freeListEligible()) {
+ // The free list is heuristics, it doesn't have to be perfect, so we can
+ // ignore the OOM here.
+ self.text_block_free_list.append(self.base.allocator, prev) catch {};
+ }
+ }
+
+ if (text_block.next) |next| {
+ next.prev = text_block.prev;
+ }
+}
+
+fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
+ const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8;
+ const endian = self.base.options.target.cpu.arch.endian();
+
+ const offset_table_start = self.section_data_offset;
+ if (self.offset_table_size_dirty) {
+ const current_raw_size = self.offset_table_size;
+ const new_raw_size = self.offset_table_size * 2;
+ log.debug("growing offset table from raw size {} to {}\n", .{ current_raw_size, new_raw_size });
+
+ // Move the text section to a new place in the executable
+ const current_text_section_start = self.section_data_offset + current_raw_size;
+ const new_text_section_start = self.section_data_offset + new_raw_size;
+
+ const amt = try self.base.file.?.copyRangeAll(current_text_section_start, self.base.file.?, new_text_section_start, self.text_section_size);
+ if (amt != self.text_section_size) return error.InputOutput;
+
+ // Write the new raw size in the .got header
+ var buf: [8]u8 = undefined;
+ mem.writeIntLittle(u32, buf[0..4], new_raw_size);
+ try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 16);
+ // Write the new .text section file offset in the .text section header
+ mem.writeIntLittle(u32, buf[0..4], new_text_section_start);
+ try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 20);
+
+ const current_virtual_size = mem.alignForwardGeneric(u32, self.offset_table_size, section_alignment);
+ const new_virtual_size = mem.alignForwardGeneric(u32, new_raw_size, section_alignment);
+ // If we had to move in the virtual address space, we need to fix the VAs in the offset table, as well as the virtual address of the `.text` section
+ // and the virutal size of the `.got` section
+
+ if (new_virtual_size != current_virtual_size) {
+ log.debug("growing offset table from virtual size {} to {}\n", .{ current_virtual_size, new_virtual_size });
+ self.size_of_image_dirty = true;
+ const va_offset = new_virtual_size - current_virtual_size;
+
+ // Write .got virtual size
+ mem.writeIntLittle(u32, buf[0..4], new_virtual_size);
+ try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 8);
+
+ // Write .text new virtual address
+ self.text_section_virtual_address = self.text_section_virtual_address + va_offset;
+ mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - image_base);
+ try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12);
+
+ // Fix the VAs in the offset table
+ for (self.offset_table.items) |*va, idx| {
+ if (va.* != 0) {
+ va.* += va_offset;
+
+ switch (entry_size) {
+ 4 => {
+ mem.writeInt(u32, buf[0..4], @intCast(u32, va.*), endian);
+ try self.base.file.?.pwriteAll(buf[0..4], offset_table_start + idx * entry_size);
+ },
+ 8 => {
+ mem.writeInt(u64, &buf, va.*, endian);
+ try self.base.file.?.pwriteAll(&buf, offset_table_start + idx * entry_size);
+ },
+ else => unreachable,
+ }
+ }
+ }
+ }
+ self.offset_table_size = new_raw_size;
+ self.offset_table_size_dirty = false;
+ }
+ // Write the new entry
+ switch (entry_size) {
+ 4 => {
+ var buf: [4]u8 = undefined;
+ mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
+ try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size);
+ },
+ 8 => {
+ var buf: [8]u8 = undefined;
+ mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
+ try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size);
+ },
+ else => unreachable,
+ }
+}
+
+pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
+ // TODO COFF/PE debug information
+ // TODO Implement exports
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ var code_buffer = std.ArrayList(u8).init(self.base.allocator);
+ defer code_buffer.deinit();
+
+ const typed_value = decl.typed_value.most_recent.typed_value;
+ const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .none);
+ const code = switch (res) {
+ .externally_managed => |x| x,
+ .appended => code_buffer.items,
+ .fail => |em| {
+ decl.analysis = .codegen_failure;
+ try module.failed_decls.put(module.gpa, decl, em);
+ return;
+ },
+ };
+
+ const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
+ const curr_size = decl.link.coff.size;
+ if (curr_size != 0) {
+ const capacity = decl.link.coff.capacity();
+ const need_realloc = code.len > capacity or
+ !mem.isAlignedGeneric(u32, decl.link.coff.text_offset, required_alignment);
+ if (need_realloc) {
+ const curr_vaddr = self.getDeclVAddr(decl);
+ const vaddr = try self.growTextBlock(&decl.link.coff, code.len, required_alignment);
+ log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, curr_vaddr, vaddr });
+ if (vaddr != curr_vaddr) {
+ log.debug(" (writing new offset table entry)\n", .{});
+ self.offset_table.items[decl.link.coff.offset_table_index] = vaddr;
+ try self.writeOffsetTableEntry(decl.link.coff.offset_table_index);
+ }
+ } else if (code.len < curr_size) {
+ self.shrinkTextBlock(&decl.link.coff, code.len);
+ }
+ } else {
+ const vaddr = try self.allocateTextBlock(&decl.link.coff, code.len, required_alignment);
+ log.debug("allocated text block for {} at 0x{x} (size: {Bi})\n", .{ mem.spanZ(decl.name), vaddr, code.len });
+ errdefer self.freeTextBlock(&decl.link.coff);
+ self.offset_table.items[decl.link.coff.offset_table_index] = vaddr;
+ try self.writeOffsetTableEntry(decl.link.coff.offset_table_index);
+ }
+
+ // Write the code into the file
+ try self.base.file.?.pwriteAll(code, self.section_data_offset + self.offset_table_size + decl.link.coff.text_offset);
+
+ // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
+ const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
+ return self.updateDeclExports(module, decl, decl_exports);
+}
+
+pub fn freeDecl(self: *Coff, decl: *Module.Decl) void {
+ // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
+ self.freeTextBlock(&decl.link.coff);
+ self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {};
+}
+
+pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, exports: []const *Module.Export) !void {
+ for (exports) |exp| {
+ if (exp.options.section) |section_name| {
+ if (!mem.eql(u8, section_name, ".text")) {
+ try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
+ module.failed_exports.putAssumeCapacityNoClobber(
+ exp,
+ try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
+ );
+ continue;
+ }
+ }
+ if (mem.eql(u8, exp.options.name, "_start")) {
+ self.entry_addr = decl.link.coff.getVAddr(self.*) - image_base;
+ } else {
+ try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
+ module.failed_exports.putAssumeCapacityNoClobber(
+ exp,
+ try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: Exports other than '_start'", .{}),
+ );
+ continue;
+ }
+ }
+}
+
+pub fn flush(self: *Coff, comp: *Compilation) !void {
+ if (build_options.have_llvm and self.base.options.use_lld) {
+ return self.linkWithLLD(comp);
+ } else {
+ switch (self.base.options.effectiveOutputMode()) {
+ .Exe, .Obj => {},
+ .Lib => return error.TODOImplementWritingLibFiles,
+ }
+ return self.flushModule(comp);
+ }
+}
+
+pub fn flushModule(self: *Coff, comp: *Compilation) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ if (self.text_section_size_dirty) {
+ // Write the new raw size in the .text header
+ var buf: [4]u8 = undefined;
+ mem.writeIntLittle(u32, &buf, self.text_section_size);
+ try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 16);
+ try self.base.file.?.setEndPos(self.section_data_offset + self.offset_table_size + self.text_section_size);
+ self.text_section_size_dirty = false;
+ }
+
+ if (self.base.options.output_mode == .Exe and self.size_of_image_dirty) {
+ const new_size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + self.text_section_size, section_alignment);
+ var buf: [4]u8 = undefined;
+ mem.writeIntLittle(u32, &buf, new_size_of_image);
+ try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56);
+ self.size_of_image_dirty = false;
+ }
+
+ if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
+ log.debug("flushing. no_entry_point_found = true\n", .{});
+ self.error_flags.no_entry_point_found = true;
+ } else {
+ log.debug("flushing. no_entry_point_found = false\n", .{});
+ self.error_flags.no_entry_point_found = false;
+
+ if (self.base.options.output_mode == .Exe) {
+ // Write AddressOfEntryPoint
+ var buf: [4]u8 = undefined;
+ mem.writeIntLittle(u32, &buf, self.entry_addr.?);
+ try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 16);
+ }
+ }
+}
+
+fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
+
+ // If there is no Zig code to compile, then we should skip flushing the output file because it
+ // will not be part of the linker line anyway.
+ const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
+ const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm;
+ if (use_stage1) {
+ const obj_basename = try std.zig.binNameAlloc(arena, .{
+ .root_name = self.base.options.root_name,
+ .target = self.base.options.target,
+ .output_mode = .Obj,
+ });
+ const o_directory = self.base.options.module.?.zig_cache_artifact_directory;
+ const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename});
+ break :blk full_obj_path;
+ }
+
+ try self.flushModule(comp);
+ const obj_basename = self.base.intermediary_basename.?;
+ const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename});
+ break :blk full_obj_path;
+ } else null;
+
+ const is_lib = self.base.options.output_mode == .Lib;
+ const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
+ const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
+ const link_in_crt = self.base.options.link_libc and self.base.options.output_mode == .Exe;
+ const target = self.base.options.target;
+
+ // See link/Elf.zig for comments on how this mechanism works.
+ const id_symlink_basename = "lld.id";
+
+ var man: Cache.Manifest = undefined;
+ defer if (!self.base.options.disable_lld_caching) man.deinit();
+
+ var digest: [Cache.hex_digest_len]u8 = undefined;
+
+ if (!self.base.options.disable_lld_caching) {
+ man = comp.cache_parent.obtain();
+ self.base.releaseLock();
+
+ try man.addListOfFiles(self.base.options.objects);
+ for (comp.c_object_table.items()) |entry| {
+ _ = try man.addFile(entry.key.status.success.object_path, null);
+ }
+ try man.addOptionalFile(module_obj_path);
+ man.hash.addOptional(self.base.options.stack_size_override);
+ man.hash.addListOfBytes(self.base.options.extra_lld_args);
+ man.hash.addListOfBytes(self.base.options.lib_dirs);
+ man.hash.add(self.base.options.is_compiler_rt_or_libc);
+ if (self.base.options.link_libc) {
+ man.hash.add(self.base.options.libc_installation != null);
+ if (self.base.options.libc_installation) |libc_installation| {
+ man.hash.addBytes(libc_installation.crt_dir.?);
+ if (target.abi == .msvc) {
+ man.hash.addBytes(libc_installation.msvc_lib_dir.?);
+ man.hash.addBytes(libc_installation.kernel32_lib_dir.?);
+ }
+ }
+ }
+ man.hash.addStringSet(self.base.options.system_libs);
+ man.hash.addOptional(self.base.options.subsystem);
+ man.hash.add(self.base.options.is_test);
+
+ // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
+ _ = try man.hit();
+ digest = man.final();
+ var prev_digest_buf: [digest.len]u8 = undefined;
+ const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {
+ log.debug("COFF LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) });
+ // Handle this as a cache miss.
+ break :blk prev_digest_buf[0..0];
+ };
+ if (mem.eql(u8, prev_digest, &digest)) {
+ log.debug("COFF LLD digest={} match - skipping invocation", .{digest});
+ // Hot diggity dog! The output binary is already there.
+ self.base.lock = man.toOwnedLock();
+ return;
+ }
+ log.debug("COFF LLD prev_digest={} new_digest={}", .{ prev_digest, digest });
+
+ // We are about to change the output file to be different, so we invalidate the build hash now.
+ directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
+ error.FileNotFound => {},
+ else => |e| return e,
+ };
+ }
+
+ const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
+
+ if (self.base.options.output_mode == .Obj) {
+ // LLD's COFF driver does not support the equvialent of `-r` so we do a simple file copy
+ // here. TODO: think carefully about how we can avoid this redundant operation when doing
+ // build-obj. See also the corresponding TODO in linkAsArchive.
+ const the_object_path = blk: {
+ if (self.base.options.objects.len != 0)
+ break :blk self.base.options.objects[0];
+
+ if (comp.c_object_table.count() != 0)
+ break :blk comp.c_object_table.items()[0].key.status.success.object_path;
+
+ if (module_obj_path) |p|
+ break :blk p;
+
+ // TODO I think this is unreachable. Audit this situation when solving the above TODO
+ // regarding eliding redundant object -> object transformations.
+ return error.NoObjectsToLink;
+ };
+ // This can happen when using --enable-cache and using the stage1 backend. In this case
+ // we can skip the file copy.
+ if (!mem.eql(u8, the_object_path, full_out_path)) {
+ try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
+ }
+ } else {
+ // Create an LLD command line and invoke it.
+ var argv = std.ArrayList([]const u8).init(self.base.allocator);
+ defer argv.deinit();
+ // Even though we're calling LLD as a library it thinks the first argument is its own exe name.
+ try argv.append("lld");
+
+ try argv.append("-ERRORLIMIT:0");
+ try argv.append("-NOLOGO");
+ if (!self.base.options.strip) {
+ try argv.append("-DEBUG");
+ }
+ if (self.base.options.output_mode == .Exe) {
+ const stack_size = self.base.options.stack_size_override orelse 16777216;
+ try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size}));
+ }
+
+ if (target.cpu.arch == .i386) {
+ try argv.append("-MACHINE:X86");
+ } else if (target.cpu.arch == .x86_64) {
+ try argv.append("-MACHINE:X64");
+ } else if (target.cpu.arch.isARM()) {
+ if (target.cpu.arch.ptrBitWidth() == 32) {
+ try argv.append("-MACHINE:ARM");
+ } else {
+ try argv.append("-MACHINE:ARM64");
+ }
+ }
+
+ if (is_dyn_lib) {
+ try argv.append("-DLL");
+ }
+
+ try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path}));
+
+ if (self.base.options.link_libc) {
+ if (self.base.options.libc_installation) |libc_installation| {
+ try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?}));
+
+ if (target.abi == .msvc) {
+ try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.msvc_lib_dir.?}));
+ try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.kernel32_lib_dir.?}));
+ }
+ }
+ }
+
+ for (self.base.options.lib_dirs) |lib_dir| {
+ try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_dir}));
+ }
+
+ try argv.appendSlice(self.base.options.objects);
+
+ for (comp.c_object_table.items()) |entry| {
+ try argv.append(entry.key.status.success.object_path);
+ }
+
+ if (module_obj_path) |p| {
+ try argv.append(p);
+ }
+
+ const resolved_subsystem: ?std.Target.SubSystem = blk: {
+ if (self.base.options.subsystem) |explicit| break :blk explicit;
+ switch (target.os.tag) {
+ .windows => {
+ if (self.base.options.module) |module| {
+ if (module.stage1_flags.have_dllmain_crt_startup or is_dyn_lib)
+ break :blk null;
+ if (module.stage1_flags.have_c_main or self.base.options.is_test or
+ module.stage1_flags.have_winmain_crt_startup or
+ module.stage1_flags.have_wwinmain_crt_startup)
+ {
+ break :blk .Console;
+ }
+ if (module.stage1_flags.have_winmain or module.stage1_flags.have_wwinmain)
+ break :blk .Windows;
+ }
+ },
+ .uefi => break :blk .EfiApplication,
+ else => {},
+ }
+ break :blk null;
+ };
+ const Mode = enum { uefi, win32 };
+ const mode: Mode = mode: {
+ if (resolved_subsystem) |subsystem| switch (subsystem) {
+ .Console => {
+ try argv.append("-SUBSYSTEM:console");
+ break :mode .win32;
+ },
+ .EfiApplication => {
+ try argv.append("-SUBSYSTEM:efi_application");
+ break :mode .uefi;
+ },
+ .EfiBootServiceDriver => {
+ try argv.append("-SUBSYSTEM:efi_boot_service_driver");
+ break :mode .uefi;
+ },
+ .EfiRom => {
+ try argv.append("-SUBSYSTEM:efi_rom");
+ break :mode .uefi;
+ },
+ .EfiRuntimeDriver => {
+ try argv.append("-SUBSYSTEM:efi_runtime_driver");
+ break :mode .uefi;
+ },
+ .Native => {
+ try argv.append("-SUBSYSTEM:native");
+ break :mode .win32;
+ },
+ .Posix => {
+ try argv.append("-SUBSYSTEM:posix");
+ break :mode .win32;
+ },
+ .Windows => {
+ try argv.append("-SUBSYSTEM:windows");
+ break :mode .win32;
+ },
+ } else if (target.os.tag == .uefi) {
+ break :mode .uefi;
+ } else {
+ break :mode .win32;
+ }
+ };
+
+ switch (mode) {
+ .uefi => try argv.appendSlice(&[_][]const u8{
+ "-BASE:0",
+ "-ENTRY:EfiMain",
+ "-OPT:REF",
+ "-SAFESEH:NO",
+ "-MERGE:.rdata=.data",
+ "-ALIGN:32",
+ "-NODEFAULTLIB",
+ "-SECTION:.xdata,D",
+ }),
+ .win32 => {
+ if (link_in_crt) {
+ if (target.abi.isGnu()) {
+ try argv.append("-lldmingw");
+
+ if (target.cpu.arch == .i386) {
+ try argv.append("-ALTERNATENAME:__image_base__=___ImageBase");
+ } else {
+ try argv.append("-ALTERNATENAME:__image_base__=__ImageBase");
+ }
+
+ if (is_dyn_lib) {
+ try argv.append(try comp.get_libc_crt_file(arena, "dllcrt2.o"));
+ } else {
+ try argv.append(try comp.get_libc_crt_file(arena, "crt2.o"));
+ }
+
+ try argv.append(try comp.get_libc_crt_file(arena, "mingw32.lib"));
+ try argv.append(try comp.get_libc_crt_file(arena, "mingwex.lib"));
+ try argv.append(try comp.get_libc_crt_file(arena, "msvcrt-os.lib"));
+
+ for (mingw.always_link_libs) |name| {
+ if (!self.base.options.system_libs.contains(name)) {
+ const lib_basename = try allocPrint(arena, "{s}.lib", .{name});
+ try argv.append(try comp.get_libc_crt_file(arena, lib_basename));
+ }
+ }
+ } else {
+ const lib_str = switch (self.base.options.link_mode) {
+ .Dynamic => "",
+ .Static => "lib",
+ };
+ const d_str = switch (self.base.options.optimize_mode) {
+ .Debug => "d",
+ else => "",
+ };
+ switch (self.base.options.link_mode) {
+ .Static => try argv.append(try allocPrint(arena, "libcmt{s}.lib", .{d_str})),
+ .Dynamic => try argv.append(try allocPrint(arena, "msvcrt{s}.lib", .{d_str})),
+ }
+
+ try argv.append(try allocPrint(arena, "{s}vcruntime{s}.lib", .{ lib_str, d_str }));
+ try argv.append(try allocPrint(arena, "{s}ucrt{s}.lib", .{ lib_str, d_str }));
+
+ //Visual C++ 2015 Conformance Changes
+ //https://msdn.microsoft.com/en-us/library/bb531344.aspx
+ try argv.append("legacy_stdio_definitions.lib");
+
+ // msvcrt depends on kernel32 and ntdll
+ try argv.append("kernel32.lib");
+ try argv.append("ntdll.lib");
+ }
+ } else {
+ try argv.append("-NODEFAULTLIB");
+ if (!is_lib) {
+ if (self.base.options.module) |module| {
+ if (module.stage1_flags.have_winmain) {
+ try argv.append("-ENTRY:WinMain");
+ } else if (module.stage1_flags.have_wwinmain) {
+ try argv.append("-ENTRY:wWinMain");
+ } else if (module.stage1_flags.have_wwinmain_crt_startup) {
+ try argv.append("-ENTRY:wWinMainCRTStartup");
+ } else {
+ try argv.append("-ENTRY:WinMainCRTStartup");
+ }
+ } else {
+ try argv.append("-ENTRY:WinMainCRTStartup");
+ }
+ }
+ }
+ },
+ }
+
+ // libc++ dep
+ if (self.base.options.link_libcpp) {
+ try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
+ try argv.append(comp.libcxx_static_lib.?.full_object_path);
+ try argv.append(comp.libunwind_static_lib.?.full_object_path);
+ }
+
+ // compiler-rt and libc
+ if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc) {
+ if (!self.base.options.link_libc) {
+ try argv.append(comp.libc_static_lib.?.full_object_path);
+ }
+ // MSVC compiler_rt is missing some stuff, so we build it unconditionally but
+ // and rely on weak linkage to allow MSVC compiler_rt functions to override ours.
+ try argv.append(comp.compiler_rt_static_lib.?.full_object_path);
+ }
+
+ for (self.base.options.system_libs.items()) |entry| {
+ const lib_basename = try allocPrint(arena, "{s}.lib", .{entry.key});
+ if (comp.crt_files.get(lib_basename)) |crt_file| {
+ try argv.append(crt_file.full_object_path);
+ } else {
+ try argv.append(lib_basename);
+ }
+ }
+
+ if (self.base.options.verbose_link) {
+ Compilation.dump_argv(argv.items);
+ }
+
+ const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null);
+ for (argv.items) |arg, i| {
+ new_argv[i] = try arena.dupeZ(u8, arg);
+ }
+
+ var stderr_context: LLDContext = .{
+ .coff = self,
+ .data = std.ArrayList(u8).init(self.base.allocator),
+ };
+ defer stderr_context.data.deinit();
+ var stdout_context: LLDContext = .{
+ .coff = self,
+ .data = std.ArrayList(u8).init(self.base.allocator),
+ };
+ defer stdout_context.data.deinit();
+ const llvm = @import("../llvm.zig");
+ const ok = llvm.Link(
+ .COFF,
+ new_argv.ptr,
+ new_argv.len,
+ append_diagnostic,
+ @ptrToInt(&stdout_context),
+ @ptrToInt(&stderr_context),
+ );
+ if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;
+ if (stdout_context.data.items.len != 0) {
+ std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});
+ }
+ if (!ok) {
+ // TODO parse this output and surface with the Compilation API rather than
+ // directly outputting to stderr here.
+ std.debug.print("{}", .{stderr_context.data.items});
+ return error.LLDReportedFailure;
+ }
+ if (stderr_context.data.items.len != 0) {
+ std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
+ }
+ }
+
+ if (!self.base.options.disable_lld_caching) {
+ // Update the dangling symlink with the digest. If it fails we can continue; it only
+ // means that the next invocation will have an unnecessary cache miss.
+ directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
+ std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});
+ };
+ // Again failure here only means an unnecessary cache miss.
+ man.writeManifest() catch |err| {
+ std.log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)});
+ };
+ // We hang on to this lock so that the output file path can be used without
+ // other processes clobbering it.
+ self.base.lock = man.toOwnedLock();
+ }
+}
+
+const LLDContext = struct {
+ data: std.ArrayList(u8),
+ coff: *Coff,
+ oom: bool = false,
+};
+
+fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
+ const lld_context = @intToPtr(*LLDContext, context);
+ const msg = ptr[0..len];
+ lld_context.data.appendSlice(msg) catch |err| switch (err) {
+ error.OutOfMemory => lld_context.oom = true,
+ };
+}
+
+pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 {
+ return self.text_section_virtual_address + decl.link.coff.text_offset;
+}
+
+pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void {
+ // TODO Implement this
+}
+
+pub fn deinit(self: *Coff) void {
+ self.text_block_free_list.deinit(self.base.allocator);
+ self.offset_table.deinit(self.base.allocator);
+ self.offset_table_free_list.deinit(self.base.allocator);
+}
diff --git a/src-self-hosted/link/Elf.zig b/src/link/Elf.zig
similarity index 83%
rename from src-self-hosted/link/Elf.zig
rename to src/link/Elf.zig
index e5acde947c..38b9b1acca 100644
--- a/src-self-hosted/link/Elf.zig
+++ b/src/link/Elf.zig
@@ -1,23 +1,29 @@
+const Elf = @This();
+
const std = @import("std");
const mem = std.mem;
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
-const ir = @import("../ir.zig");
-const Module = @import("../Module.zig");
const fs = std.fs;
const elf = std.elf;
-const codegen = @import("../codegen.zig");
const log = std.log.scoped(.link);
const DW = std.dwarf;
-const trace = @import("../tracy.zig").trace;
const leb128 = std.debug.leb;
+
+const ir = @import("../ir.zig");
+const Module = @import("../Module.zig");
+const Compilation = @import("../Compilation.zig");
+const codegen = @import("../codegen.zig");
+const trace = @import("../tracy.zig").trace;
const Package = @import("../Package.zig");
const Value = @import("../value.zig").Value;
const Type = @import("../type.zig").Type;
const link = @import("../link.zig");
const File = link.File;
-const Elf = @This();
const build_options = @import("build_options");
+const target_util = @import("../target.zig");
+const glibc = @import("../glibc.zig");
+const Cache = @import("../Cache.zig");
const default_entry_addr = 0x8000000;
@@ -28,7 +34,7 @@ pub const base_tag: File.Tag = .elf;
base: File,
-ptr_width: enum { p32, p64 },
+ptr_width: PtrWidth,
/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
/// Same order as in the file.
@@ -130,6 +136,8 @@ const alloc_den = 3;
const minimum_text_block_size = 64;
const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;
+pub const PtrWidth = enum { p32, p64 };
+
pub const TextBlock = struct {
/// Each decl always gets a local symbol with the fully qualified name.
/// The vaddr and size are found here directly.
@@ -216,74 +224,23 @@ pub const SrcFn = struct {
};
};
-pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File {
+pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Elf {
assert(options.object_format == .elf);
- const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) });
+ if (options.use_llvm) return error.LLVMBackendUnimplementedForELF; // TODO
+
+ const file = try options.emit.?.directory.handle.createFile(sub_path, .{
+ .truncate = false,
+ .read = true,
+ .mode = link.determineMode(options),
+ });
errdefer file.close();
- var elf_file = try allocator.create(Elf);
- errdefer allocator.destroy(elf_file);
+ const self = try createEmpty(allocator, options);
+ errdefer self.base.destroy();
- elf_file.* = openFile(allocator, file, options) catch |err| switch (err) {
- error.IncrFailed => try createFile(allocator, file, options),
- else => |e| return e,
- };
-
- return &elf_file.base;
-}
-
-/// Returns error.IncrFailed if incremental update could not be performed.
-fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf {
- switch (options.output_mode) {
- .Exe => {},
- .Obj => {},
- .Lib => return error.IncrFailed,
- }
- var self: Elf = .{
- .base = .{
- .file = file,
- .tag = .elf,
- .options = options,
- .allocator = allocator,
- },
- .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
- 0 ... 32 => .p32,
- 33 ... 64 => .p64,
- else => return error.UnsupportedELFArchitecture,
- },
- };
- errdefer self.deinit();
-
- // TODO implement reading the elf file
- return error.IncrFailed;
- //try self.populateMissingMetadata();
- //return self;
-}
-
-/// Truncates the existing file contents and overwrites the contents.
-/// Returns an error if `file` is not already open with +read +write +seek abilities.
-fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf {
- switch (options.output_mode) {
- .Exe => {},
- .Obj => {},
- .Lib => return error.TODOImplementWritingLibFiles,
- }
- var self: Elf = .{
- .base = .{
- .tag = .elf,
- .options = options,
- .allocator = allocator,
- .file = file,
- },
- .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
- 0 ... 32 => .p32,
- 33 ... 64 => .p64,
- else => return error.UnsupportedELFArchitecture,
- },
- .shdr_table_dirty = true,
- };
- errdefer self.deinit();
+ self.base.file = file;
+ self.shdr_table_dirty = true;
// Index 0 is always a null symbol.
try self.local_symbols.append(allocator, .{
@@ -314,6 +271,25 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf
return self;
}
+pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf {
+ const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) {
+ 0 ... 32 => .p32,
+ 33 ... 64 => .p64,
+ else => return error.UnsupportedELFArchitecture,
+ };
+ const self = try gpa.create(Elf);
+ self.* = .{
+ .base = .{
+ .tag = .elf,
+ .options = options,
+ .allocator = gpa,
+ .file = null,
+ },
+ .ptr_width = ptr_width,
+ };
+ return self;
+}
+
pub fn deinit(self: *Elf) void {
self.sections.deinit(self.base.allocator);
self.program_headers.deinit(self.base.allocator);
@@ -738,8 +714,26 @@ pub const abbrev_base_type = 4;
pub const abbrev_pad1 = 5;
pub const abbrev_parameter = 6;
-/// Commit pending changes and write headers.
-pub fn flush(self: *Elf, module: *Module) !void {
+pub fn flush(self: *Elf, comp: *Compilation) !void {
+ if (build_options.have_llvm and self.base.options.use_lld) {
+ return self.linkWithLLD(comp);
+ } else {
+ switch (self.base.options.effectiveOutputMode()) {
+ .Exe, .Obj => {},
+ .Lib => return error.TODOImplementWritingLibFiles,
+ }
+ return self.flushModule(comp);
+ }
+}
+
+pub fn flushModule(self: *Elf, comp: *Compilation) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the
+ // Zig source code.
+ const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
+
const target_endian = self.base.options.target.cpu.arch.endian();
const foreign_endian = target_endian != std.Target.current.cpu.arch.endian();
const ptr_width_bytes: u8 = self.ptrWidthBytes();
@@ -861,8 +855,8 @@ pub fn flush(self: *Elf, module: *Module) !void {
},
}
// Write the form for the compile unit, which must match the abbrev table above.
- const name_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_path);
- const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_dir_path);
+ const name_strp = try self.makeDebugString(module.root_pkg.root_src_path);
+ const comp_dir_strp = try self.makeDebugString(module.root_pkg.root_src_directory.path orelse ".");
const producer_strp = try self.makeDebugString(link.producer_string);
// Currently only one compilation unit is supported, so the address range is simply
// identical to the main program header virtual address and memory size.
@@ -1031,7 +1025,7 @@ pub fn flush(self: *Elf, module: *Module) !void {
0, // include_directories (none except the compilation unit cwd)
});
// file_names[0]
- di_buf.appendSliceAssumeCapacity(self.base.options.root_pkg.root_src_path); // relative path name
+ di_buf.appendSliceAssumeCapacity(module.root_pkg.root_src_path); // relative path name
di_buf.appendSliceAssumeCapacity(&[_]u8{
0, // null byte for the relative path name
0, // directory_index
@@ -1195,7 +1189,7 @@ pub fn flush(self: *Elf, module: *Module) !void {
}
self.shdr_table_dirty = false;
}
- if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
+ if (self.entry_addr == null and self.base.options.effectiveOutputMode() == .Exe) {
log.debug("flushing. no_entry_point_found = true\n", .{});
self.error_flags.no_entry_point_found = true;
} else {
@@ -1216,6 +1210,449 @@ pub fn flush(self: *Elf, module: *Module) !void {
assert(!self.debug_strtab_dirty);
}
+fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
+
+ // If there is no Zig code to compile, then we should skip flushing the output file because it
+ // will not be part of the linker line anyway.
+ const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
+ const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm;
+ if (use_stage1) {
+ const obj_basename = try std.zig.binNameAlloc(arena, .{
+ .root_name = self.base.options.root_name,
+ .target = self.base.options.target,
+ .output_mode = .Obj,
+ });
+ const o_directory = self.base.options.module.?.zig_cache_artifact_directory;
+ const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename});
+ break :blk full_obj_path;
+ }
+
+ try self.flushModule(comp);
+ const obj_basename = self.base.intermediary_basename.?;
+ const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename});
+ break :blk full_obj_path;
+ } else null;
+
+ const is_obj = self.base.options.output_mode == .Obj;
+ const is_lib = self.base.options.output_mode == .Lib;
+ const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
+ const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
+ const have_dynamic_linker = self.base.options.link_libc and
+ self.base.options.link_mode == .Dynamic and is_exe_or_dyn_lib;
+ const link_in_crt = self.base.options.link_libc and self.base.options.output_mode == .Exe;
+ const target = self.base.options.target;
+ const gc_sections = self.base.options.gc_sections orelse !is_obj;
+ const stack_size = self.base.options.stack_size_override orelse 16777216;
+ const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os;
+
+ // Here we want to determine whether we can save time by not invoking LLD when the
+ // output is unchanged. None of the linker options or the object files that are being
+ // linked are in the hash that namespaces the directory we are outputting to. Therefore,
+ // we must hash those now, and the resulting digest will form the "id" of the linking
+ // job we are about to perform.
+ // After a successful link, we store the id in the metadata of a symlink named "id.txt" in
+ // the artifact directory. So, now, we check if this symlink exists, and if it matches
+ // our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD.
+ const id_symlink_basename = "lld.id";
+
+ var man: Cache.Manifest = undefined;
+ defer if (!self.base.options.disable_lld_caching) man.deinit();
+
+ var digest: [Cache.hex_digest_len]u8 = undefined;
+
+ if (!self.base.options.disable_lld_caching) {
+ man = comp.cache_parent.obtain();
+
+ // We are about to obtain this lock, so here we give other processes a chance first.
+ self.base.releaseLock();
+
+ try man.addOptionalFile(self.base.options.linker_script);
+ try man.addOptionalFile(self.base.options.version_script);
+ try man.addListOfFiles(self.base.options.objects);
+ for (comp.c_object_table.items()) |entry| {
+ _ = try man.addFile(entry.key.status.success.object_path, null);
+ }
+ try man.addOptionalFile(module_obj_path);
+ // We can skip hashing libc and libc++ components that we are in charge of building from Zig
+ // installation sources because they are always a product of the compiler version + target information.
+ man.hash.add(stack_size);
+ man.hash.add(gc_sections);
+ man.hash.add(self.base.options.eh_frame_hdr);
+ man.hash.add(self.base.options.rdynamic);
+ man.hash.addListOfBytes(self.base.options.extra_lld_args);
+ man.hash.addListOfBytes(self.base.options.lib_dirs);
+ man.hash.addListOfBytes(self.base.options.rpath_list);
+ man.hash.add(self.base.options.each_lib_rpath);
+ man.hash.add(self.base.options.is_compiler_rt_or_libc);
+ man.hash.add(self.base.options.z_nodelete);
+ man.hash.add(self.base.options.z_defs);
+ if (self.base.options.link_libc) {
+ man.hash.add(self.base.options.libc_installation != null);
+ if (self.base.options.libc_installation) |libc_installation| {
+ man.hash.addBytes(libc_installation.crt_dir.?);
+ }
+ if (have_dynamic_linker) {
+ man.hash.addOptionalBytes(self.base.options.dynamic_linker);
+ }
+ }
+ if (is_dyn_lib) {
+ man.hash.addOptionalBytes(self.base.options.override_soname);
+ man.hash.addOptional(self.base.options.version);
+ }
+ man.hash.addStringSet(self.base.options.system_libs);
+ man.hash.add(allow_shlib_undefined);
+ man.hash.add(self.base.options.bind_global_refs_locally);
+
+ // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
+ _ = try man.hit();
+ digest = man.final();
+
+ var prev_digest_buf: [digest.len]u8 = undefined;
+ const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {
+ log.debug("ELF LLD new_digest={} readlink error: {}", .{digest, @errorName(err)});
+ // Handle this as a cache miss.
+ break :blk prev_digest_buf[0..0];
+ };
+ if (mem.eql(u8, prev_digest, &digest)) {
+ log.debug("ELF LLD digest={} match - skipping invocation", .{digest});
+ // Hot diggity dog! The output binary is already there.
+ self.base.lock = man.toOwnedLock();
+ return;
+ }
+ log.debug("ELF LLD prev_digest={} new_digest={}", .{prev_digest, digest});
+
+ // We are about to change the output file to be different, so we invalidate the build hash now.
+ directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
+ error.FileNotFound => {},
+ else => |e| return e,
+ };
+ }
+
+ // Create an LLD command line and invoke it.
+ var argv = std.ArrayList([]const u8).init(self.base.allocator);
+ defer argv.deinit();
+ // Even though we're calling LLD as a library it thinks the first argument is its own exe name.
+ try argv.append("lld");
+ if (is_obj) {
+ try argv.append("-r");
+ }
+
+ try argv.append("-error-limit=0");
+
+ if (self.base.options.output_mode == .Exe) {
+ try argv.append("-z");
+ try argv.append(try std.fmt.allocPrint(arena, "stack-size={}", .{stack_size}));
+ }
+
+ if (self.base.options.linker_script) |linker_script| {
+ try argv.append("-T");
+ try argv.append(linker_script);
+ }
+
+ if (gc_sections) {
+ try argv.append("--gc-sections");
+ }
+
+ if (self.base.options.eh_frame_hdr) {
+ try argv.append("--eh-frame-hdr");
+ }
+
+ if (self.base.options.rdynamic) {
+ try argv.append("--export-dynamic");
+ }
+
+ try argv.appendSlice(self.base.options.extra_lld_args);
+
+ if (self.base.options.z_nodelete) {
+ try argv.append("-z");
+ try argv.append("nodelete");
+ }
+ if (self.base.options.z_defs) {
+ try argv.append("-z");
+ try argv.append("defs");
+ }
+
+ if (getLDMOption(target)) |ldm| {
+ // Any target ELF will use the freebsd osabi if suffixed with "_fbsd".
+ const arg = if (target.os.tag == .freebsd)
+ try std.fmt.allocPrint(arena, "{}_fbsd", .{ldm})
+ else
+ ldm;
+ try argv.append("-m");
+ try argv.append(arg);
+ }
+
+ if (self.base.options.link_mode == .Static) {
+ if (target.cpu.arch.isARM() or target.cpu.arch.isThumb()) {
+ try argv.append("-Bstatic");
+ } else {
+ try argv.append("-static");
+ }
+ } else if (is_dyn_lib) {
+ try argv.append("-shared");
+ }
+
+ if (target_util.requiresPIE(target) and self.base.options.output_mode == .Exe) {
+ try argv.append("-pie");
+ }
+
+ const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
+ try argv.append("-o");
+ try argv.append(full_out_path);
+
+ if (link_in_crt) {
+ const crt1o: []const u8 = o: {
+ if (target.os.tag == .netbsd) {
+ break :o "crt0.o";
+ } else if (target.isAndroid()) {
+ if (self.base.options.link_mode == .Dynamic) {
+ break :o "crtbegin_dynamic.o";
+ } else {
+ break :o "crtbegin_static.o";
+ }
+ } else if (self.base.options.link_mode == .Static) {
+ break :o "crt1.o";
+ } else {
+ break :o "Scrt1.o";
+ }
+ };
+ try argv.append(try comp.get_libc_crt_file(arena, crt1o));
+ if (target_util.libc_needs_crti_crtn(target)) {
+ try argv.append(try comp.get_libc_crt_file(arena, "crti.o"));
+ }
+ }
+
+ // rpaths
+ var rpath_table = std.StringHashMap(void).init(self.base.allocator);
+ defer rpath_table.deinit();
+ for (self.base.options.rpath_list) |rpath| {
+ if ((try rpath_table.fetchPut(rpath, {})) == null) {
+ try argv.append("-rpath");
+ try argv.append(rpath);
+ }
+ }
+ if (self.base.options.each_lib_rpath) {
+ var test_path = std.ArrayList(u8).init(self.base.allocator);
+ defer test_path.deinit();
+ for (self.base.options.lib_dirs) |lib_dir_path| {
+ for (self.base.options.system_libs.items()) |link_lib| {
+ test_path.shrinkRetainingCapacity(0);
+ const sep = fs.path.sep_str;
+ try test_path.writer().print("{}" ++ sep ++ "lib{}.so", .{ lib_dir_path, link_lib });
+ fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
+ error.FileNotFound => continue,
+ else => |e| return e,
+ };
+ if ((try rpath_table.fetchPut(lib_dir_path, {})) == null) {
+ try argv.append("-rpath");
+ try argv.append(lib_dir_path);
+ }
+ }
+ }
+ }
+
+ for (self.base.options.lib_dirs) |lib_dir| {
+ try argv.append("-L");
+ try argv.append(lib_dir);
+ }
+
+ if (self.base.options.link_libc) {
+ if (self.base.options.libc_installation) |libc_installation| {
+ try argv.append("-L");
+ try argv.append(libc_installation.crt_dir.?);
+ }
+
+ if (have_dynamic_linker) {
+ if (self.base.options.dynamic_linker) |dynamic_linker| {
+ try argv.append("-dynamic-linker");
+ try argv.append(dynamic_linker);
+ }
+ }
+ }
+
+ if (is_dyn_lib) {
+ const soname = self.base.options.override_soname orelse if (self.base.options.version) |ver|
+ try std.fmt.allocPrint(arena, "lib{}.so.{}", .{self.base.options.root_name, ver.major})
+ else
+ try std.fmt.allocPrint(arena, "lib{}.so", .{self.base.options.root_name});
+ try argv.append("-soname");
+ try argv.append(soname);
+
+ if (self.base.options.version_script) |version_script| {
+ try argv.append("-version-script");
+ try argv.append(version_script);
+ }
+ }
+
+ // Positional arguments to the linker such as object files.
+ try argv.appendSlice(self.base.options.objects);
+
+ for (comp.c_object_table.items()) |entry| {
+ try argv.append(entry.key.status.success.object_path);
+ }
+
+ if (module_obj_path) |p| {
+ try argv.append(p);
+ }
+
+ // compiler-rt and libc
+ if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc) {
+ if (!self.base.options.link_libc) {
+ try argv.append(comp.libc_static_lib.?.full_object_path);
+ }
+ try argv.append(comp.compiler_rt_static_lib.?.full_object_path);
+ }
+
+ // Shared libraries.
+ const system_libs = self.base.options.system_libs.items();
+ try argv.ensureCapacity(argv.items.len + system_libs.len);
+ for (system_libs) |entry| {
+ const link_lib = entry.key;
+ // By this time, we depend on these libs being dynamically linked libraries and not static libraries
+ // (the check for that needs to be earlier), but they could be full paths to .so files, in which
+ // case we want to avoid prepending "-l".
+ const ext = Compilation.classifyFileExt(link_lib);
+ const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib});
+ argv.appendAssumeCapacity(arg);
+ }
+
+ if (!is_obj) {
+ // libc++ dep
+ if (self.base.options.link_libcpp) {
+ try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
+ try argv.append(comp.libcxx_static_lib.?.full_object_path);
+ }
+
+ // libc dep
+ if (self.base.options.link_libc) {
+ if (self.base.options.libc_installation != null) {
+ if (self.base.options.link_mode == .Static) {
+ try argv.append("--start-group");
+ try argv.append("-lc");
+ try argv.append("-lm");
+ try argv.append("--end-group");
+ } else {
+ try argv.append("-lc");
+ try argv.append("-lm");
+ }
+
+ if (target.os.tag == .freebsd or target.os.tag == .netbsd) {
+ try argv.append("-lpthread");
+ }
+ } else if (target.isGnuLibC()) {
+ try argv.append(comp.libunwind_static_lib.?.full_object_path);
+ for (glibc.libs) |lib| {
+ const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
+ comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
+ });
+ try argv.append(lib_path);
+ }
+ try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a"));
+ } else if (target.isMusl()) {
+ try argv.append(comp.libunwind_static_lib.?.full_object_path);
+ try argv.append(try comp.get_libc_crt_file(arena, "libc.a"));
+ } else if (self.base.options.link_libcpp) {
+ try argv.append(comp.libunwind_static_lib.?.full_object_path);
+ } else {
+ unreachable; // Compiler was supposed to emit an error for not being able to provide libc.
+ }
+ }
+ }
+
+ // crt end
+ if (link_in_crt) {
+ if (target.isAndroid()) {
+ try argv.append(try comp.get_libc_crt_file(arena, "crtend_android.o"));
+ } else if (target_util.libc_needs_crti_crtn(target)) {
+ try argv.append(try comp.get_libc_crt_file(arena, "crtn.o"));
+ }
+ }
+
+ if (allow_shlib_undefined) {
+ try argv.append("--allow-shlib-undefined");
+ }
+
+ if (self.base.options.bind_global_refs_locally) {
+ try argv.append("-Bsymbolic");
+ }
+
+ if (self.base.options.verbose_link) {
+ Compilation.dump_argv(argv.items);
+ }
+
+ // Oh, snapplesauce! We need null terminated argv.
+ const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null);
+ for (argv.items) |arg, i| {
+ new_argv[i] = try arena.dupeZ(u8, arg);
+ }
+
+ var stderr_context: LLDContext = .{
+ .elf = self,
+ .data = std.ArrayList(u8).init(self.base.allocator),
+ };
+ defer stderr_context.data.deinit();
+ var stdout_context: LLDContext = .{
+ .elf = self,
+ .data = std.ArrayList(u8).init(self.base.allocator),
+ };
+ defer stdout_context.data.deinit();
+ const llvm = @import("../llvm.zig");
+ const ok = llvm.Link(.ELF, new_argv.ptr, new_argv.len, append_diagnostic,
+ @ptrToInt(&stdout_context),
+ @ptrToInt(&stderr_context),
+ );
+ if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;
+ if (stdout_context.data.items.len != 0) {
+ std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});
+ }
+ if (!ok) {
+ // TODO parse this output and surface with the Compilation API rather than
+ // directly outputting to stderr here.
+ std.debug.print("{}", .{stderr_context.data.items});
+ return error.LLDReportedFailure;
+ }
+ if (stderr_context.data.items.len != 0) {
+ std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
+ }
+
+ if (!self.base.options.disable_lld_caching) {
+ // Update the dangling symlink with the digest. If it fails we can continue; it only
+ // means that the next invocation will have an unnecessary cache miss.
+ directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
+ std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});
+ };
+ // Again failure here only means an unnecessary cache miss.
+ man.writeManifest() catch |err| {
+ std.log.warn("failed to write cache manifest when linking: {}", .{ @errorName(err) });
+ };
+ // We hang on to this lock so that the output file path can be used without
+ // other processes clobbering it.
+ self.base.lock = man.toOwnedLock();
+ }
+}
+
+const LLDContext = struct {
+ data: std.ArrayList(u8),
+ elf: *Elf,
+ oom: bool = false,
+};
+
+fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
+ const lld_context = @intToPtr(*LLDContext, context);
+ const msg = ptr[0..len];
+ lld_context.data.appendSlice(msg) catch |err| switch (err) {
+ error.OutOfMemory => lld_context.oom = true,
+ };
+}
+
fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void {
const target_endian = self.base.options.target.cpu.arch.endian();
switch (self.ptr_width) {
@@ -1255,7 +1692,7 @@ fn writeElfHeader(self: *Elf) !void {
assert(index == 16);
- const elf_type = switch (self.base.options.output_mode) {
+ const elf_type = switch (self.base.options.effectiveOutputMode()) {
.Exe => elf.ET.EXEC,
.Obj => elf.ET.REL,
.Lib => switch (self.base.options.link_mode) {
@@ -2104,7 +2541,7 @@ pub fn updateDeclExports(
try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
module.failed_exports.putAssumeCapacityNoClobber(
exp,
- try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
+ try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
);
continue;
}
@@ -2122,7 +2559,7 @@ pub fn updateDeclExports(
try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
module.failed_exports.putAssumeCapacityNoClobber(
exp,
- try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
+ try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
);
continue;
},
@@ -2426,12 +2863,13 @@ fn dbgLineNeededHeaderBytes(self: Elf) u32 {
const file_name_entry_format_count = 1;
const directory_count = 1;
const file_name_count = 1;
+ const root_src_dir_path_len = if (self.base.options.module.?.root_pkg.root_src_directory.path) |p| p.len else 1; // "."
return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 +
directory_count * 8 + file_name_count * 8 +
// These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like
// because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.
- self.base.options.root_pkg.root_src_dir_path.len +
- self.base.options.root_pkg.root_src_path.len);
+ root_src_dir_path_len +
+ self.base.options.module.?.root_pkg.root_src_path.len);
}
fn dbgInfoNeededHeaderBytes(self: Elf) u32 {
@@ -2630,3 +3068,33 @@ fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
.sh_entsize = @intCast(u32, shdr.sh_entsize),
};
}
+
+fn getLDMOption(target: std.Target) ?[]const u8 {
+ switch (target.cpu.arch) {
+ .i386 => return "elf_i386",
+ .aarch64 => return "aarch64linux",
+ .aarch64_be => return "aarch64_be_linux",
+ .arm, .thumb => return "armelf_linux_eabi",
+ .armeb, .thumbeb => return "armebelf_linux_eabi",
+ .powerpc => return "elf32ppclinux",
+ .powerpc64 => return "elf64ppc",
+ .powerpc64le => return "elf64lppc",
+ .sparc, .sparcel => return "elf32_sparc",
+ .sparcv9 => return "elf64_sparc",
+ .mips => return "elf32btsmip",
+ .mipsel => return "elf32ltsmip",
+ .mips64 => return "elf64btsmip",
+ .mips64el => return "elf64ltsmip",
+ .s390x => return "elf64_s390",
+ .x86_64 => {
+ if (target.abi == .gnux32) {
+ return "elf32_x86_64";
+ } else {
+ return "elf_x86_64";
+ }
+ },
+ .riscv32 => return "elf32lriscv",
+ .riscv64 => return "elf64lriscv",
+ else => return null,
+ }
+}
diff --git a/src-self-hosted/link/MachO.zig b/src/link/MachO.zig
similarity index 59%
rename from src-self-hosted/link/MachO.zig
rename to src/link/MachO.zig
index 13932e514a..961b64a840 100644
--- a/src-self-hosted/link/MachO.zig
+++ b/src/link/MachO.zig
@@ -9,12 +9,16 @@ const macho = std.macho;
const codegen = @import("../codegen.zig");
const math = std.math;
const mem = std.mem;
+
const trace = @import("../tracy.zig").trace;
const Type = @import("../type.zig").Type;
-
+const build_options = @import("build_options");
const Module = @import("../Module.zig");
+const Compilation = @import("../Compilation.zig");
const link = @import("../link.zig");
const File = link.File;
+const Cache = @import("../Cache.zig");
+const target_util = @import("../target.zig");
pub const base_tag: File.Tag = File.Tag.macho;
@@ -134,71 +138,64 @@ pub const SrcFn = struct {
pub const empty = SrcFn{};
};
-pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File {
+pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*MachO {
assert(options.object_format == .macho);
- const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) });
+ if (options.use_llvm) return error.LLVM_BackendIsTODO_ForMachO; // TODO
+ if (options.use_lld) return error.LLD_LinkingIsTODO_ForMachO; // TODO
+
+ const file = try options.emit.?.directory.handle.createFile(sub_path, .{
+ .truncate = false,
+ .read = true,
+ .mode = link.determineMode(options),
+ });
errdefer file.close();
- var macho_file = try allocator.create(MachO);
- errdefer allocator.destroy(macho_file);
+ const self = try createEmpty(allocator, options);
+ errdefer self.base.destroy();
- macho_file.* = openFile(allocator, file, options) catch |err| switch (err) {
- error.IncrFailed => try createFile(allocator, file, options),
- else => |e| return e,
- };
+ self.base.file = file;
- return &macho_file.base;
-}
-
-/// Returns error.IncrFailed if incremental update could not be performed.
-fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO {
- switch (options.output_mode) {
- .Exe => {},
- .Obj => {},
- .Lib => return error.IncrFailed,
- }
- var self: MachO = .{
- .base = .{
- .file = file,
- .tag = .macho,
- .options = options,
- .allocator = allocator,
- },
- };
- errdefer self.deinit();
-
- // TODO implement reading the macho file
- return error.IncrFailed;
- //try self.populateMissingMetadata();
- //return self;
-}
-
-/// Truncates the existing file contents and overwrites the contents.
-/// Returns an error if `file` is not already open with +read +write +seek abilities.
-fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO {
switch (options.output_mode) {
.Exe => {},
.Obj => {},
.Lib => return error.TODOImplementWritingLibFiles,
}
- var self: MachO = .{
- .base = .{
- .file = file,
- .tag = .macho,
- .options = options,
- .allocator = allocator,
- },
- };
- errdefer self.deinit();
-
try self.populateMissingMetadata();
return self;
}
-pub fn flush(self: *MachO, module: *Module) !void {
+pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO {
+ const self = try gpa.create(MachO);
+ self.* = .{
+ .base = .{
+ .tag = .macho,
+ .options = options,
+ .allocator = gpa,
+ .file = null,
+ },
+ };
+ return self;
+}
+
+pub fn flush(self: *MachO, comp: *Compilation) !void {
+ if (build_options.have_llvm and self.base.options.use_lld) {
+ return self.linkWithLLD(comp);
+ } else {
+ switch (self.base.options.effectiveOutputMode()) {
+ .Exe, .Obj => {},
+ .Lib => return error.TODOImplementWritingLibFiles,
+ }
+ return self.flushModule(comp);
+ }
+}
+
+pub fn flushModule(self: *MachO, comp: *Compilation) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
switch (self.base.options.output_mode) {
.Exe => {
var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
@@ -291,6 +288,384 @@ pub fn flush(self: *MachO, module: *Module) !void {
}
}
+fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
+
+ // If there is no Zig code to compile, then we should skip flushing the output file because it
+ // will not be part of the linker line anyway.
+ const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
+ const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm;
+ if (use_stage1) {
+ const obj_basename = try std.zig.binNameAlloc(arena, .{
+ .root_name = self.base.options.root_name,
+ .target = self.base.options.target,
+ .output_mode = .Obj,
+ });
+ const o_directory = self.base.options.module.?.zig_cache_artifact_directory;
+ const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename});
+ break :blk full_obj_path;
+ }
+
+ try self.flushModule(comp);
+ const obj_basename = self.base.intermediary_basename.?;
+ const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename});
+ break :blk full_obj_path;
+ } else null;
+
+ const is_lib = self.base.options.output_mode == .Lib;
+ const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
+ const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
+ const target = self.base.options.target;
+ const stack_size = self.base.options.stack_size_override orelse 16777216;
+ const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os;
+
+ const id_symlink_basename = "lld.id";
+
+ var man: Cache.Manifest = undefined;
+ defer if (!self.base.options.disable_lld_caching) man.deinit();
+
+ var digest: [Cache.hex_digest_len]u8 = undefined;
+
+ if (!self.base.options.disable_lld_caching) {
+ man = comp.cache_parent.obtain();
+
+ // We are about to obtain this lock, so here we give other processes a chance first.
+ self.base.releaseLock();
+
+ try man.addOptionalFile(self.base.options.linker_script);
+ try man.addOptionalFile(self.base.options.version_script);
+ try man.addListOfFiles(self.base.options.objects);
+ for (comp.c_object_table.items()) |entry| {
+ _ = try man.addFile(entry.key.status.success.object_path, null);
+ }
+ try man.addOptionalFile(module_obj_path);
+ // We can skip hashing libc and libc++ components that we are in charge of building from Zig
+ // installation sources because they are always a product of the compiler version + target information.
+ man.hash.add(stack_size);
+ man.hash.add(self.base.options.rdynamic);
+ man.hash.addListOfBytes(self.base.options.extra_lld_args);
+ man.hash.addListOfBytes(self.base.options.lib_dirs);
+ man.hash.addListOfBytes(self.base.options.framework_dirs);
+ man.hash.addListOfBytes(self.base.options.frameworks);
+ man.hash.addListOfBytes(self.base.options.rpath_list);
+ man.hash.add(self.base.options.is_compiler_rt_or_libc);
+ man.hash.add(self.base.options.z_nodelete);
+ man.hash.add(self.base.options.z_defs);
+ if (is_dyn_lib) {
+ man.hash.addOptional(self.base.options.version);
+ }
+ man.hash.addStringSet(self.base.options.system_libs);
+ man.hash.add(allow_shlib_undefined);
+ man.hash.add(self.base.options.bind_global_refs_locally);
+
+ // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
+ _ = try man.hit();
+ digest = man.final();
+
+ var prev_digest_buf: [digest.len]u8 = undefined;
+ const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {
+ log.debug("MachO LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) });
+ // Handle this as a cache miss.
+ break :blk prev_digest_buf[0..0];
+ };
+ if (mem.eql(u8, prev_digest, &digest)) {
+ log.debug("MachO LLD digest={} match - skipping invocation", .{digest});
+ // Hot diggity dog! The output binary is already there.
+ self.base.lock = man.toOwnedLock();
+ return;
+ }
+ log.debug("MachO LLD prev_digest={} new_digest={}", .{ prev_digest, digest });
+
+ // We are about to change the output file to be different, so we invalidate the build hash now.
+ directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
+ error.FileNotFound => {},
+ else => |e| return e,
+ };
+ }
+
+ const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
+
+ if (self.base.options.output_mode == .Obj) {
+ // LLD's MachO driver does not support the equvialent of `-r` so we do a simple file copy
+ // here. TODO: think carefully about how we can avoid this redundant operation when doing
+ // build-obj. See also the corresponding TODO in linkAsArchive.
+ const the_object_path = blk: {
+ if (self.base.options.objects.len != 0)
+ break :blk self.base.options.objects[0];
+
+ if (comp.c_object_table.count() != 0)
+ break :blk comp.c_object_table.items()[0].key.status.success.object_path;
+
+ if (module_obj_path) |p|
+ break :blk p;
+
+ // TODO I think this is unreachable. Audit this situation when solving the above TODO
+ // regarding eliding redundant object -> object transformations.
+ return error.NoObjectsToLink;
+ };
+ // This can happen when using --enable-cache and using the stage1 backend. In this case
+ // we can skip the file copy.
+ if (!mem.eql(u8, the_object_path, full_out_path)) {
+ try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
+ }
+ } else {
+ // Create an LLD command line and invoke it.
+ var argv = std.ArrayList([]const u8).init(self.base.allocator);
+ defer argv.deinit();
+ // Even though we're calling LLD as a library it thinks the first argument is its own exe name.
+ try argv.append("lld");
+
+ try argv.append("-error-limit");
+ try argv.append("0");
+
+ try argv.append("-demangle");
+
+ if (self.base.options.rdynamic) {
+ try argv.append("--export-dynamic");
+ }
+
+ try argv.appendSlice(self.base.options.extra_lld_args);
+
+ if (self.base.options.z_nodelete) {
+ try argv.append("-z");
+ try argv.append("nodelete");
+ }
+ if (self.base.options.z_defs) {
+ try argv.append("-z");
+ try argv.append("defs");
+ }
+
+ if (is_dyn_lib) {
+ try argv.append("-static");
+ } else {
+ try argv.append("-dynamic");
+ }
+
+ if (is_dyn_lib) {
+ try argv.append("-dylib");
+
+ if (self.base.options.version) |ver| {
+ const compat_vers = try std.fmt.allocPrint(arena, "{d}.0.0", .{ver.major});
+ try argv.append("-compatibility_version");
+ try argv.append(compat_vers);
+
+ const cur_vers = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch });
+ try argv.append("-current_version");
+ try argv.append(cur_vers);
+ }
+
+ // TODO getting an error when running an executable when doing this rpath thing
+ //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib",
+ // buf_ptr(g->root_out_name), g->version_major);
+ //try argv.append("-install_name");
+ //try argv.append(buf_ptr(dylib_install_name));
+ }
+
+ try argv.append("-arch");
+ try argv.append(darwinArchString(target.cpu.arch));
+
+ switch (target.os.tag) {
+ .macosx => {
+ try argv.append("-macosx_version_min");
+ },
+ .ios, .tvos, .watchos => switch (target.cpu.arch) {
+ .i386, .x86_64 => {
+ try argv.append("-ios_simulator_version_min");
+ },
+ else => {
+ try argv.append("-iphoneos_version_min");
+ },
+ },
+ else => unreachable,
+ }
+ const ver = target.os.version_range.semver.min;
+ const version_string = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch });
+ try argv.append(version_string);
+
+ try argv.append("-sdk_version");
+ try argv.append(version_string);
+
+ if (target_util.requiresPIE(target) and self.base.options.output_mode == .Exe) {
+ try argv.append("-pie");
+ }
+
+ try argv.append("-o");
+ try argv.append(full_out_path);
+
+ // rpaths
+ var rpath_table = std.StringHashMap(void).init(self.base.allocator);
+ defer rpath_table.deinit();
+ for (self.base.options.rpath_list) |rpath| {
+ if ((try rpath_table.fetchPut(rpath, {})) == null) {
+ try argv.append("-rpath");
+ try argv.append(rpath);
+ }
+ }
+ if (is_dyn_lib) {
+ if ((try rpath_table.fetchPut(full_out_path, {})) == null) {
+ try argv.append("-rpath");
+ try argv.append(full_out_path);
+ }
+ }
+
+ for (self.base.options.lib_dirs) |lib_dir| {
+ try argv.append("-L");
+ try argv.append(lib_dir);
+ }
+
+ // Positional arguments to the linker such as object files.
+ try argv.appendSlice(self.base.options.objects);
+
+ for (comp.c_object_table.items()) |entry| {
+ try argv.append(entry.key.status.success.object_path);
+ }
+ if (module_obj_path) |p| {
+ try argv.append(p);
+ }
+
+ // compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce
+ if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc) {
+ try argv.append(comp.compiler_rt_static_lib.?.full_object_path);
+ }
+
+ // Shared libraries.
+ const system_libs = self.base.options.system_libs.items();
+ try argv.ensureCapacity(argv.items.len + system_libs.len);
+ for (system_libs) |entry| {
+ const link_lib = entry.key;
+ // By this time, we depend on these libs being dynamically linked libraries and not static libraries
+ // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
+ // case we want to avoid prepending "-l".
+ const ext = Compilation.classifyFileExt(link_lib);
+ const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib});
+ argv.appendAssumeCapacity(arg);
+ }
+
+ // libc++ dep
+ if (self.base.options.link_libcpp) {
+ try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
+ try argv.append(comp.libcxx_static_lib.?.full_object_path);
+ }
+
+ // On Darwin, libSystem has libc in it, but also you have to use it
+ // to make syscalls because the syscall numbers are not documented
+ // and change between versions. So we always link against libSystem.
+ // LLD craps out if you do -lSystem cross compiling, so until that
+ // codebase gets some love from the new maintainers we're left with
+ // this dirty hack.
+ if (self.base.options.is_native_os) {
+ try argv.append("-lSystem");
+ }
+
+ for (self.base.options.framework_dirs) |framework_dir| {
+ try argv.append("-F");
+ try argv.append(framework_dir);
+ }
+ for (self.base.options.frameworks) |framework| {
+ try argv.append("-framework");
+ try argv.append(framework);
+ }
+
+ if (allow_shlib_undefined) {
+ try argv.append("-undefined");
+ try argv.append("dynamic_lookup");
+ }
+ if (self.base.options.bind_global_refs_locally) {
+ try argv.append("-Bsymbolic");
+ }
+
+ if (self.base.options.verbose_link) {
+ Compilation.dump_argv(argv.items);
+ }
+
+ const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null);
+ for (argv.items) |arg, i| {
+ new_argv[i] = try arena.dupeZ(u8, arg);
+ }
+
+ var stderr_context: LLDContext = .{
+ .macho = self,
+ .data = std.ArrayList(u8).init(self.base.allocator),
+ };
+ defer stderr_context.data.deinit();
+ var stdout_context: LLDContext = .{
+ .macho = self,
+ .data = std.ArrayList(u8).init(self.base.allocator),
+ };
+ defer stdout_context.data.deinit();
+ const llvm = @import("../llvm.zig");
+ const ok = llvm.Link(
+ .MachO,
+ new_argv.ptr,
+ new_argv.len,
+ append_diagnostic,
+ @ptrToInt(&stdout_context),
+ @ptrToInt(&stderr_context),
+ );
+ if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;
+ if (stdout_context.data.items.len != 0) {
+ std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});
+ }
+ if (!ok) {
+ // TODO parse this output and surface with the Compilation API rather than
+ // directly outputting to stderr here.
+ std.debug.print("{}", .{stderr_context.data.items});
+ return error.LLDReportedFailure;
+ }
+ if (stderr_context.data.items.len != 0) {
+ std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
+ }
+ }
+
+ if (!self.base.options.disable_lld_caching) {
+ // Update the dangling symlink with the digest. If it fails we can continue; it only
+ // means that the next invocation will have an unnecessary cache miss.
+ directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
+ std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});
+ };
+ // Again failure here only means an unnecessary cache miss.
+ man.writeManifest() catch |err| {
+ std.log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)});
+ };
+ // We hang on to this lock so that the output file path can be used without
+ // other processes clobbering it.
+ self.base.lock = man.toOwnedLock();
+ }
+}
+
+const LLDContext = struct {
+ data: std.ArrayList(u8),
+ macho: *MachO,
+ oom: bool = false,
+};
+
+fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
+ const lld_context = @intToPtr(*LLDContext, context);
+ const msg = ptr[0..len];
+ lld_context.data.appendSlice(msg) catch |err| switch (err) {
+ error.OutOfMemory => lld_context.oom = true,
+ };
+}
+
+fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {
+ return switch (arch) {
+ .aarch64, .aarch64_be, .aarch64_32 => "arm64",
+ .thumb, .arm => "arm",
+ .thumbeb, .armeb => "armeb",
+ .powerpc => "ppc",
+ .powerpc64 => "ppc64",
+ .powerpc64le => "ppc64le",
+ else => @tagName(arch),
+ };
+}
+
pub fn deinit(self: *MachO) void {
self.offset_table.deinit(self.base.allocator);
self.string_table.deinit(self.base.allocator);
diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig
new file mode 100644
index 0000000000..3f879a3b32
--- /dev/null
+++ b/src/link/Wasm.zig
@@ -0,0 +1,479 @@
+const Wasm = @This();
+
+const std = @import("std");
+const mem = std.mem;
+const Allocator = std.mem.Allocator;
+const assert = std.debug.assert;
+const fs = std.fs;
+const leb = std.debug.leb;
+const log = std.log.scoped(.link);
+
+const Module = @import("../Module.zig");
+const Compilation = @import("../Compilation.zig");
+const codegen = @import("../codegen/wasm.zig");
+const link = @import("../link.zig");
+const trace = @import("../tracy.zig").trace;
+const build_options = @import("build_options");
+const Cache = @import("../Cache.zig");
+
+/// Various magic numbers defined by the wasm spec
+const spec = struct {
+ const magic = [_]u8{ 0x00, 0x61, 0x73, 0x6D }; // \0asm
+ const version = [_]u8{ 0x01, 0x00, 0x00, 0x00 }; // version 1
+
+ const custom_id = 0;
+ const types_id = 1;
+ const imports_id = 2;
+ const funcs_id = 3;
+ const tables_id = 4;
+ const memories_id = 5;
+ const globals_id = 6;
+ const exports_id = 7;
+ const start_id = 8;
+ const elements_id = 9;
+ const code_id = 10;
+ const data_id = 11;
+};
+
+pub const base_tag = link.File.Tag.wasm;
+
+pub const FnData = struct {
+ /// Generated code for the type of the function
+ functype: std.ArrayListUnmanaged(u8) = .{},
+ /// Generated code for the body of the function
+ code: std.ArrayListUnmanaged(u8) = .{},
+ /// Locations in the generated code where function indexes must be filled in.
+ /// This must be kept ordered by offset.
+ idx_refs: std.ArrayListUnmanaged(struct { offset: u32, decl: *Module.Decl }) = .{},
+};
+
+base: link.File,
+
+/// List of all function Decls to be written to the output file. The index of
+/// each Decl in this list at the time of writing the binary is used as the
+/// function index.
+/// TODO: can/should we access some data structure in Module directly?
+funcs: std.ArrayListUnmanaged(*Module.Decl) = .{},
+
+pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm {
+ assert(options.object_format == .wasm);
+
+ if (options.use_llvm) return error.LLVM_BackendIsTODO_ForWasm; // TODO
+ if (options.use_lld) return error.LLD_LinkingIsTODO_ForWasm; // TODO
+
+ // TODO: read the file and keep vaild parts instead of truncating
+ const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });
+ errdefer file.close();
+
+ const wasm = try createEmpty(allocator, options);
+ errdefer wasm.base.destroy();
+
+ wasm.base.file = file;
+
+ try file.writeAll(&(spec.magic ++ spec.version));
+
+ return wasm;
+}
+
+pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Wasm {
+ const wasm = try gpa.create(Wasm);
+ wasm.* = .{
+ .base = .{
+ .tag = .wasm,
+ .options = options,
+ .file = null,
+ .allocator = gpa,
+ },
+ };
+ return wasm;
+}
+
+pub fn deinit(self: *Wasm) void {
+ for (self.funcs.items) |decl| {
+ decl.fn_link.wasm.?.functype.deinit(self.base.allocator);
+ decl.fn_link.wasm.?.code.deinit(self.base.allocator);
+ decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator);
+ }
+ self.funcs.deinit(self.base.allocator);
+}
+
+// Generate code for the Decl, storing it in memory to be later written to
+// the file on flush().
+pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
+ if (decl.typed_value.most_recent.typed_value.ty.zigTypeTag() != .Fn)
+ return error.TODOImplementNonFnDeclsForWasm;
+
+ if (decl.fn_link.wasm) |*fn_data| {
+ fn_data.functype.items.len = 0;
+ fn_data.code.items.len = 0;
+ fn_data.idx_refs.items.len = 0;
+ } else {
+ decl.fn_link.wasm = .{};
+ try self.funcs.append(self.base.allocator, decl);
+ }
+ const fn_data = &decl.fn_link.wasm.?;
+
+ var managed_functype = fn_data.functype.toManaged(self.base.allocator);
+ var managed_code = fn_data.code.toManaged(self.base.allocator);
+ try codegen.genFunctype(&managed_functype, decl);
+ try codegen.genCode(&managed_code, decl);
+ fn_data.functype = managed_functype.toUnmanaged();
+ fn_data.code = managed_code.toUnmanaged();
+}
+
+pub fn updateDeclExports(
+ self: *Wasm,
+ module: *Module,
+ decl: *const Module.Decl,
+ exports: []const *Module.Export,
+) !void {}
+
+pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
+ // TODO: remove this assert when non-function Decls are implemented
+ assert(decl.typed_value.most_recent.typed_value.ty.zigTypeTag() == .Fn);
+ _ = self.funcs.swapRemove(self.getFuncidx(decl).?);
+ decl.fn_link.wasm.?.functype.deinit(self.base.allocator);
+ decl.fn_link.wasm.?.code.deinit(self.base.allocator);
+ decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator);
+ decl.fn_link.wasm = null;
+}
+
+pub fn flush(self: *Wasm, comp: *Compilation) !void {
+ if (build_options.have_llvm and self.base.options.use_lld) {
+ return self.linkWithLLD(comp);
+ } else {
+ return self.flushModule(comp);
+ }
+}
+
+pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const file = self.base.file.?;
+ const header_size = 5 + 1;
+
+ // No need to rewrite the magic/version header
+ try file.setEndPos(@sizeOf(@TypeOf(spec.magic ++ spec.version)));
+ try file.seekTo(@sizeOf(@TypeOf(spec.magic ++ spec.version)));
+
+ // Type section
+ {
+ const header_offset = try reserveVecSectionHeader(file);
+ for (self.funcs.items) |decl| {
+ try file.writeAll(decl.fn_link.wasm.?.functype.items);
+ }
+ try writeVecSectionHeader(
+ file,
+ header_offset,
+ spec.types_id,
+ @intCast(u32, (try file.getPos()) - header_offset - header_size),
+ @intCast(u32, self.funcs.items.len),
+ );
+ }
+
+ // Function section
+ {
+ const header_offset = try reserveVecSectionHeader(file);
+ const writer = file.writer();
+ for (self.funcs.items) |_, typeidx| try leb.writeULEB128(writer, @intCast(u32, typeidx));
+ try writeVecSectionHeader(
+ file,
+ header_offset,
+ spec.funcs_id,
+ @intCast(u32, (try file.getPos()) - header_offset - header_size),
+ @intCast(u32, self.funcs.items.len),
+ );
+ }
+
+ // Export section
+ if (self.base.options.module) |module| {
+ const header_offset = try reserveVecSectionHeader(file);
+ const writer = file.writer();
+ var count: u32 = 0;
+ for (module.decl_exports.entries.items) |entry| {
+ for (entry.value) |exprt| {
+ // Export name length + name
+ try leb.writeULEB128(writer, @intCast(u32, exprt.options.name.len));
+ try writer.writeAll(exprt.options.name);
+
+ switch (exprt.exported_decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
+ .Fn => {
+ // Type of the export
+ try writer.writeByte(0x00);
+ // Exported function index
+ try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).?);
+ },
+ else => return error.TODOImplementNonFnDeclsForWasm,
+ }
+
+ count += 1;
+ }
+ }
+ try writeVecSectionHeader(
+ file,
+ header_offset,
+ spec.exports_id,
+ @intCast(u32, (try file.getPos()) - header_offset - header_size),
+ count,
+ );
+ }
+
+ // Code section
+ {
+ const header_offset = try reserveVecSectionHeader(file);
+ const writer = file.writer();
+ for (self.funcs.items) |decl| {
+ const fn_data = &decl.fn_link.wasm.?;
+
+ // Write the already generated code to the file, inserting
+ // function indexes where required.
+ var current: u32 = 0;
+ for (fn_data.idx_refs.items) |idx_ref| {
+ try writer.writeAll(fn_data.code.items[current..idx_ref.offset]);
+ current = idx_ref.offset;
+ // Use a fixed width here to make calculating the code size
+ // in codegen.wasm.genCode() simpler.
+ var buf: [5]u8 = undefined;
+ leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).?);
+ try writer.writeAll(&buf);
+ }
+
+ try writer.writeAll(fn_data.code.items[current..]);
+ }
+ try writeVecSectionHeader(
+ file,
+ header_offset,
+ spec.code_id,
+ @intCast(u32, (try file.getPos()) - header_offset - header_size),
+ @intCast(u32, self.funcs.items.len),
+ );
+ }
+}
+
+fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
+
+ // If there is no Zig code to compile, then we should skip flushing the output file because it
+ // will not be part of the linker line anyway.
+ const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
+ const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm;
+ if (use_stage1) {
+ const obj_basename = try std.zig.binNameAlloc(arena, .{
+ .root_name = self.base.options.root_name,
+ .target = self.base.options.target,
+ .output_mode = .Obj,
+ });
+ const o_directory = self.base.options.module.?.zig_cache_artifact_directory;
+ const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename});
+ break :blk full_obj_path;
+ }
+
+ try self.flushModule(comp);
+ const obj_basename = self.base.intermediary_basename.?;
+ const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename});
+ break :blk full_obj_path;
+ } else null;
+
+ const target = self.base.options.target;
+
+ const id_symlink_basename = "lld.id";
+
+ var man: Cache.Manifest = undefined;
+ defer if (!self.base.options.disable_lld_caching) man.deinit();
+
+ var digest: [Cache.hex_digest_len]u8 = undefined;
+
+ if (!self.base.options.disable_lld_caching) {
+ man = comp.cache_parent.obtain();
+
+ // We are about to obtain this lock, so here we give other processes a chance first.
+ self.base.releaseLock();
+
+ try man.addListOfFiles(self.base.options.objects);
+ for (comp.c_object_table.items()) |entry| {
+ _ = try man.addFile(entry.key.status.success.object_path, null);
+ }
+ try man.addOptionalFile(module_obj_path);
+ man.hash.addOptional(self.base.options.stack_size_override);
+ man.hash.addListOfBytes(self.base.options.extra_lld_args);
+
+ // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
+ _ = try man.hit();
+ digest = man.final();
+
+ var prev_digest_buf: [digest.len]u8 = undefined;
+ const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {
+ log.debug("WASM LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) });
+ // Handle this as a cache miss.
+ break :blk prev_digest_buf[0..0];
+ };
+ if (mem.eql(u8, prev_digest, &digest)) {
+ log.debug("WASM LLD digest={} match - skipping invocation", .{digest});
+ // Hot diggity dog! The output binary is already there.
+ self.base.lock = man.toOwnedLock();
+ return;
+ }
+ log.debug("WASM LLD prev_digest={} new_digest={}", .{ prev_digest, digest });
+
+ // We are about to change the output file to be different, so we invalidate the build hash now.
+ directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
+ error.FileNotFound => {},
+ else => |e| return e,
+ };
+ }
+
+ const is_obj = self.base.options.output_mode == .Obj;
+
+ // Create an LLD command line and invoke it.
+ var argv = std.ArrayList([]const u8).init(self.base.allocator);
+ defer argv.deinit();
+ // Even though we're calling LLD as a library it thinks the first argument is its own exe name.
+ try argv.append("lld");
+ if (is_obj) {
+ try argv.append("-r");
+ }
+
+ try argv.append("-error-limit=0");
+
+ if (self.base.options.output_mode == .Exe) {
+ // Increase the default stack size to a more reasonable value of 1MB instead of
+ // the default of 1 Wasm page being 64KB, unless overriden by the user.
+ try argv.append("-z");
+ const stack_size = self.base.options.stack_size_override orelse 1048576;
+ const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size});
+ try argv.append(arg);
+
+ // Put stack before globals so that stack overflow results in segfault immediately
+ // before corrupting globals. See https://github.com/ziglang/zig/issues/4496
+ try argv.append("--stack-first");
+ } else {
+ try argv.append("--no-entry"); // So lld doesn't look for _start.
+ try argv.append("--export-all");
+ }
+ try argv.appendSlice(&[_][]const u8{
+ "--allow-undefined",
+ "-o",
+ try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}),
+ });
+
+ // Positional arguments to the linker such as object files.
+ try argv.appendSlice(self.base.options.objects);
+
+ for (comp.c_object_table.items()) |entry| {
+ try argv.append(entry.key.status.success.object_path);
+ }
+ if (module_obj_path) |p| {
+ try argv.append(p);
+ }
+
+ if (self.base.options.output_mode == .Exe and !self.base.options.is_compiler_rt_or_libc) {
+ if (!self.base.options.link_libc) {
+ try argv.append(comp.libc_static_lib.?.full_object_path);
+ }
+ try argv.append(comp.compiler_rt_static_lib.?.full_object_path);
+ }
+
+ if (self.base.options.verbose_link) {
+ Compilation.dump_argv(argv.items);
+ }
+
+ const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null);
+ for (argv.items) |arg, i| {
+ new_argv[i] = try arena.dupeZ(u8, arg);
+ }
+
+ var stderr_context: LLDContext = .{
+ .wasm = self,
+ .data = std.ArrayList(u8).init(self.base.allocator),
+ };
+ defer stderr_context.data.deinit();
+ var stdout_context: LLDContext = .{
+ .wasm = self,
+ .data = std.ArrayList(u8).init(self.base.allocator),
+ };
+ defer stdout_context.data.deinit();
+ const llvm = @import("../llvm.zig");
+ const ok = llvm.Link(
+ .Wasm,
+ new_argv.ptr,
+ new_argv.len,
+ append_diagnostic,
+ @ptrToInt(&stdout_context),
+ @ptrToInt(&stderr_context),
+ );
+ if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;
+ if (stdout_context.data.items.len != 0) {
+ std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});
+ }
+ if (!ok) {
+ // TODO parse this output and surface with the Compilation API rather than
+ // directly outputting to stderr here.
+ std.debug.print("{}", .{stderr_context.data.items});
+ return error.LLDReportedFailure;
+ }
+ if (stderr_context.data.items.len != 0) {
+ std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
+ }
+
+ if (!self.base.options.disable_lld_caching) {
+ // Update the dangling symlink with the digest. If it fails we can continue; it only
+ // means that the next invocation will have an unnecessary cache miss.
+ directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
+ std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});
+ };
+ // Again failure here only means an unnecessary cache miss.
+ man.writeManifest() catch |err| {
+ std.log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)});
+ };
+ // We hang on to this lock so that the output file path can be used without
+ // other processes clobbering it.
+ self.base.lock = man.toOwnedLock();
+ }
+}
+
+const LLDContext = struct {
+ data: std.ArrayList(u8),
+ wasm: *Wasm,
+ oom: bool = false,
+};
+
+fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
+ const lld_context = @intToPtr(*LLDContext, context);
+ const msg = ptr[0..len];
+ lld_context.data.appendSlice(msg) catch |err| switch (err) {
+ error.OutOfMemory => lld_context.oom = true,
+ };
+}
+
+/// Get the current index of a given Decl in the function list
+/// TODO: we could maintain a hash map to potentially make this
+fn getFuncidx(self: Wasm, decl: *Module.Decl) ?u32 {
+ return for (self.funcs.items) |func, idx| {
+ if (func == decl) break @intCast(u32, idx);
+ } else null;
+}
+
+fn reserveVecSectionHeader(file: fs.File) !u64 {
+ // section id + fixed leb contents size + fixed leb vector length
+ const header_size = 1 + 5 + 5;
+ // TODO: this should be a single lseek(2) call, but fs.File does not
+ // currently provide a way to do this.
+ try file.seekBy(header_size);
+ return (try file.getPos()) - header_size;
+}
+
+fn writeVecSectionHeader(file: fs.File, offset: u64, section: u8, size: u32, items: u32) !void {
+ var buf: [1 + 5 + 5]u8 = undefined;
+ buf[0] = section;
+ leb.writeUnsignedFixed(5, buf[1..6], size);
+ leb.writeUnsignedFixed(5, buf[6..], items);
+ try file.pwriteAll(&buf, offset);
+}
diff --git a/src-self-hosted/link/cbe.h b/src/link/cbe.h
similarity index 100%
rename from src-self-hosted/link/cbe.h
rename to src/link/cbe.h
diff --git a/src-self-hosted/link/msdos-stub.bin b/src/link/msdos-stub.bin
similarity index 100%
rename from src-self-hosted/link/msdos-stub.bin
rename to src/link/msdos-stub.bin
diff --git a/src-self-hosted/liveness.zig b/src/liveness.zig
similarity index 100%
rename from src-self-hosted/liveness.zig
rename to src/liveness.zig
diff --git a/src/llvm.zig b/src/llvm.zig
new file mode 100644
index 0000000000..3aebf46b81
--- /dev/null
+++ b/src/llvm.zig
@@ -0,0 +1,140 @@
+//! We do this instead of @cImport because the self-hosted compiler is easier
+//! to bootstrap if it does not depend on translate-c.
+
+pub const Link = ZigLLDLink;
+extern fn ZigLLDLink(
+ oformat: ObjectFormatType,
+ args: [*:null]const ?[*:0]const u8,
+ arg_count: usize,
+ append_diagnostic: fn (context: usize, ptr: [*]const u8, len: usize) callconv(.C) void,
+ context_stdout: usize,
+ context_stderr: usize,
+) bool;
+
+pub const ObjectFormatType = extern enum(c_int) {
+ Unknown,
+ COFF,
+ ELF,
+ MachO,
+ Wasm,
+ XCOFF,
+};
+
+pub const GetHostCPUName = LLVMGetHostCPUName;
+extern fn LLVMGetHostCPUName() ?[*:0]u8;
+
+pub const GetNativeFeatures = ZigLLVMGetNativeFeatures;
+extern fn ZigLLVMGetNativeFeatures() ?[*:0]u8;
+
+pub const WriteArchive = ZigLLVMWriteArchive;
+extern fn ZigLLVMWriteArchive(
+ archive_name: [*:0]const u8,
+ file_names_ptr: [*]const [*:0]const u8,
+ file_names_len: usize,
+ os_type: OSType,
+) bool;
+
+pub const OSType = extern enum(c_int) {
+ UnknownOS = 0,
+ Ananas = 1,
+ CloudABI = 2,
+ Darwin = 3,
+ DragonFly = 4,
+ FreeBSD = 5,
+ Fuchsia = 6,
+ IOS = 7,
+ KFreeBSD = 8,
+ Linux = 9,
+ Lv2 = 10,
+ MacOSX = 11,
+ NetBSD = 12,
+ OpenBSD = 13,
+ Solaris = 14,
+ Win32 = 15,
+ Haiku = 16,
+ Minix = 17,
+ RTEMS = 18,
+ NaCl = 19,
+ CNK = 20,
+ AIX = 21,
+ CUDA = 22,
+ NVCL = 23,
+ AMDHSA = 24,
+ PS4 = 25,
+ ELFIAMCU = 26,
+ TvOS = 27,
+ WatchOS = 28,
+ Mesa3D = 29,
+ Contiki = 30,
+ AMDPAL = 31,
+ HermitCore = 32,
+ Hurd = 33,
+ WASI = 34,
+ Emscripten = 35,
+};
+
+pub const ArchType = extern enum(c_int) {
+ UnknownArch = 0,
+ arm = 1,
+ armeb = 2,
+ aarch64 = 3,
+ aarch64_be = 4,
+ aarch64_32 = 5,
+ arc = 6,
+ avr = 7,
+ bpfel = 8,
+ bpfeb = 9,
+ hexagon = 10,
+ mips = 11,
+ mipsel = 12,
+ mips64 = 13,
+ mips64el = 14,
+ msp430 = 15,
+ ppc = 16,
+ ppc64 = 17,
+ ppc64le = 18,
+ r600 = 19,
+ amdgcn = 20,
+ riscv32 = 21,
+ riscv64 = 22,
+ sparc = 23,
+ sparcv9 = 24,
+ sparcel = 25,
+ systemz = 26,
+ tce = 27,
+ tcele = 28,
+ thumb = 29,
+ thumbeb = 30,
+ x86 = 31,
+ x86_64 = 32,
+ xcore = 33,
+ nvptx = 34,
+ nvptx64 = 35,
+ le32 = 36,
+ le64 = 37,
+ amdil = 38,
+ amdil64 = 39,
+ hsail = 40,
+ hsail64 = 41,
+ spir = 42,
+ spir64 = 43,
+ kalimba = 44,
+ shave = 45,
+ lanai = 46,
+ wasm32 = 47,
+ wasm64 = 48,
+ renderscript32 = 49,
+ renderscript64 = 50,
+ ve = 51,
+};
+
+pub const ParseCommandLineOptions = ZigLLVMParseCommandLineOptions;
+extern fn ZigLLVMParseCommandLineOptions(argc: usize, argv: [*]const [*:0]const u8) void;
+
+pub const WriteImportLibrary = ZigLLVMWriteImportLibrary;
+extern fn ZigLLVMWriteImportLibrary(
+ def_path: [*:0]const u8,
+ arch: ArchType,
+ output_lib_path: [*c]const u8,
+ kill_at: bool,
+) bool;
diff --git a/src/main.cpp b/src/main.cpp
deleted file mode 100644
index 348321598c..0000000000
--- a/src/main.cpp
+++ /dev/null
@@ -1,1878 +0,0 @@
-/*
- * Copyright (c) 2015 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#include "ast_render.hpp"
-#include "buffer.hpp"
-#include "codegen.hpp"
-#include "compiler.hpp"
-#include "config.h"
-#include "error.hpp"
-#include "heap.hpp"
-#include "os.hpp"
-#include "target.hpp"
-#include "stage2.h"
-#include "glibc.hpp"
-#include "dump_analysis.hpp"
-#include "mem_profile.hpp"
-
-#include
-
-static int print_error_usage(const char *arg0) {
- fprintf(stderr, "See `%s --help` for detailed usage information\n", arg0);
- return EXIT_FAILURE;
-}
-
-static int print_full_usage(const char *arg0, FILE *file, int return_code) {
- fprintf(file,
- "Usage: %s [command] [options]\n"
- "\n"
- "Commands:\n"
- " build build project from build.zig\n"
- " build-exe [source] create executable from source or object files\n"
- " build-lib [source] create library from source or object files\n"
- " build-obj [source] create object from source or assembly\n"
- " builtin show the source code of @import(\"builtin\")\n"
- " cc use Zig as a drop-in C compiler\n"
- " c++ use Zig as a drop-in C++ compiler\n"
- " env print lib path, std path, compiler id and version\n"
- " fmt parse files and render in canonical zig format\n"
- " id print the base64-encoded compiler id\n"
- " init-exe initialize a `zig build` application in the cwd\n"
- " init-lib initialize a `zig build` library in the cwd\n"
- " libc [paths_file] Display native libc paths file or validate one\n"
- " run [source] [-- [args]] create executable and run immediately\n"
- " translate-c [source] convert c code to zig code\n"
- " targets list available compilation targets\n"
- " test [source] create and run a test build\n"
- " version print version number and exit\n"
- " zen print zen of zig and exit\n"
- "\n"
- "Compile Options:\n"
- " --c-source [options] [file] compile C source code\n"
- " --cache-dir [path] override the local cache directory\n"
- " --cache [auto|off|on] build in cache, print output path to stdout\n"
- " --color [auto|off|on] enable or disable colored error messages\n"
- " --disable-valgrind omit valgrind client requests in debug builds\n"
- " --eh-frame-hdr enable C++ exception handling by passing --eh-frame-hdr to linker\n"
- " --enable-valgrind include valgrind client requests release builds\n"
- " -fstack-check enable stack probing in unsafe builds\n"
- " -fno-stack-check disable stack probing in safe builds\n"
- " -fsanitize-c enable C undefined behavior detection in unsafe builds\n"
- " -fno-sanitize-c disable C undefined behavior detection in safe builds\n"
- " --emit [asm|bin|llvm-ir] (deprecated) emit a specific file format as compilation output\n"
- " -fPIC enable Position Independent Code\n"
- " -fno-PIC disable Position Independent Code\n"
- " -ftime-report print timing diagnostics\n"
- " -fstack-report print stack size diagnostics\n"
- " -fmem-report print memory usage diagnostics\n"
- " -fdump-analysis write analysis.json file with type information\n"
- " -femit-docs create a docs/ dir with html documentation\n"
- " -fno-emit-docs do not produce docs/ dir with html documentation\n"
- " -femit-bin (default) output machine code\n"
- " -fno-emit-bin do not output machine code\n"
- " -femit-asm output .s (assembly code)\n"
- " -fno-emit-asm (default) do not output .s (assembly code)\n"
- " -femit-llvm-ir produce a .ll file with LLVM IR\n"
- " -fno-emit-llvm-ir (default) do not produce a .ll file with LLVM IR\n"
- " -femit-h generate a C header file (.h)\n"
- " -fno-emit-h (default) do not generate a C header file (.h)\n"
- " --libc [file] Provide a file which specifies libc paths\n"
- " --name [name] override output name\n"
- " --output-dir [dir] override output directory (defaults to cwd)\n"
- " --pkg-begin [name] [path] make pkg available to import and push current pkg\n"
- " --pkg-end pop current pkg\n"
- " --main-pkg-path set the directory of the root package\n"
- " --release-fast build with optimizations on and safety off\n"
- " --release-safe build with optimizations on and safety on\n"
- " --release-small build with size optimizations on and safety off\n"
- " --single-threaded source may assume it is only used single-threaded\n"
- " -dynamic create a shared library (.so; .dll; .dylib)\n"
- " --strip exclude debug symbols\n"
- " -target [name] -- see the targets command\n"
- " --verbose-tokenize enable compiler debug output for tokenization\n"
- " --verbose-ast enable compiler debug output for AST parsing\n"
- " --verbose-link enable compiler debug output for linking\n"
- " --verbose-ir enable compiler debug output for Zig IR\n"
- " --verbose-llvm-ir enable compiler debug output for LLVM IR\n"
- " --verbose-cimport enable compiler debug output for C imports\n"
- " --verbose-cc enable compiler debug output for C compilation\n"
- " --verbose-llvm-cpu-features enable compiler debug output for LLVM CPU features\n"
- " -dirafter [dir] add directory to AFTER include search path\n"
- " -isystem [dir] add directory to SYSTEM include search path\n"
- " -I[dir] add directory to include search path\n"
- " -mllvm [arg] (unsupported) forward an arg to LLVM's option processing\n"
- " --override-lib-dir [arg] override path to Zig lib directory\n"
- " -ffunction-sections places each function in a separate section\n"
- " -D[macro]=[value] define C [macro] to [value] (1 if [value] omitted)\n"
- " -mcpu [cpu] specify target CPU and feature set\n"
- " -code-model [default|tiny| set target code model\n"
- " small|kernel|\n"
- " medium|large]\n"
- "\n"
- "Link Options:\n"
- " --bundle-compiler-rt for static libraries, include compiler-rt symbols\n"
- " --dynamic-linker [path] set the path to ld.so\n"
- " --each-lib-rpath add rpath for each used dynamic library\n"
- " --library [lib] link against lib\n"
- " --forbid-library [lib] make it an error to link against lib\n"
- " --library-path [dir] add a directory to the library search path\n"
- " --linker-script [path] use a custom linker script\n"
- " --version-script [path] provide a version .map file\n"
- " --object [obj] add object file to build\n"
- " -L[dir] alias for --library-path\n"
- " -l[lib] alias for --library\n"
- " -rdynamic add all symbols to the dynamic symbol table\n"
- " -rpath [path] add directory to the runtime library search path\n"
- " --stack [size] (linux, windows, Wasm) override default stack size\n"
- " --subsystem [subsystem] (windows) /SUBSYSTEM: to the linker\n"
- " -F[dir] (darwin) add search path for frameworks\n"
- " -framework [name] (darwin) link against framework\n"
- " --ver-major [ver] dynamic library semver major version\n"
- " --ver-minor [ver] dynamic library semver minor version\n"
- " --ver-patch [ver] dynamic library semver patch version\n"
- " -Bsymbolic bind global references locally\n"
- "\n"
- "Test Options:\n"
- " --test-filter [text] skip tests that do not match filter\n"
- " --test-name-prefix [text] add prefix to all tests\n"
- " --test-cmd [arg] specify test execution command one arg at a time\n"
- " --test-cmd-bin appends test binary path to test cmd args\n"
- " --test-evented-io runs the test in evented I/O mode\n"
- , arg0);
- return return_code;
-}
-
-static int print_libc_usage(const char *arg0, FILE *file, int return_code) {
- fprintf(file,
- "Usage: %s libc\n"
- "\n"
- "Detect the native libc installation and print the resulting paths to stdout.\n"
- "You can save this into a file and then edit the paths to create a cross\n"
- "compilation libc kit. Then you can pass `--libc [file]` for Zig to use it.\n"
- "\n"
- "When compiling natively and no `--libc` argument provided, Zig will create\n"
- "`%s/native_libc.txt`\n"
- "so that it does not have to detect libc on every invocation. You can remove\n"
- "this file to have Zig re-detect the native libc.\n"
- "\n\n"
- "Usage: %s libc [file]\n"
- "\n"
- "Parse a libc installation text file and validate it.\n"
- , arg0, buf_ptr(get_global_cache_dir()), arg0);
- return return_code;
-}
-
-enum Cmd {
- CmdNone,
- CmdBuild,
- CmdBuiltin,
- CmdRun,
- CmdTargets,
- CmdTest,
- CmdTranslateC,
- CmdVersion,
- CmdZen,
- CmdLibC,
-};
-
-static const char *default_zig_cache_name = "zig-cache";
-
-struct CliPkg {
- const char *name;
- const char *path;
- ZigList children;
- CliPkg *parent;
-};
-
-static void add_package(CodeGen *g, CliPkg *cli_pkg, ZigPackage *pkg) {
- for (size_t i = 0; i < cli_pkg->children.length; i += 1) {
- CliPkg *child_cli_pkg = cli_pkg->children.at(i);
-
- Buf *dirname = buf_alloc();
- Buf *basename = buf_alloc();
- os_path_split(buf_create_from_str(child_cli_pkg->path), dirname, basename);
-
- ZigPackage *child_pkg = codegen_create_package(g, buf_ptr(dirname), buf_ptr(basename),
- buf_ptr(buf_sprintf("%s.%s", buf_ptr(&pkg->pkg_path), child_cli_pkg->name)));
- auto entry = pkg->package_table.put_unique(buf_create_from_str(child_cli_pkg->name), child_pkg);
- if (entry) {
- ZigPackage *existing_pkg = entry->value;
- Buf *full_path = buf_alloc();
- os_path_join(&existing_pkg->root_src_dir, &existing_pkg->root_src_path, full_path);
- fprintf(stderr, "Unable to add package '%s'->'%s': already exists as '%s'\n",
- child_cli_pkg->name, child_cli_pkg->path, buf_ptr(full_path));
- exit(EXIT_FAILURE);
- }
-
- add_package(g, child_cli_pkg, child_pkg);
- }
-}
-
-enum CacheOpt {
- CacheOptAuto,
- CacheOptOn,
- CacheOptOff,
-};
-
-static bool get_cache_opt(CacheOpt opt, bool default_value) {
- switch (opt) {
- case CacheOptAuto:
- return default_value;
- case CacheOptOn:
- return true;
- case CacheOptOff:
- return false;
- }
- zig_unreachable();
-}
-
-static int zig_error_no_build_file(void) {
- fprintf(stderr,
- "No 'build.zig' file found, in the current directory or any parent directories.\n"
- "Initialize a 'build.zig' template file with `zig init-lib` or `zig init-exe`,\n"
- "or see `zig --help` for more options.\n"
- );
- return EXIT_FAILURE;
-}
-
-static bool str_starts_with(const char *s1, const char *s2) {
- size_t s2_len = strlen(s2);
- if (strlen(s1) < s2_len) {
- return false;
- }
- return memcmp(s1, s2, s2_len) == 0;
-}
-
-extern "C" int ZigClang_main(int argc, char **argv);
-
-#ifdef ZIG_ENABLE_MEM_PROFILE
-bool mem_report = false;
-#endif
-
-int main_exit(Stage2ProgressNode *root_progress_node, int exit_code) {
- if (root_progress_node != nullptr) {
- stage2_progress_end(root_progress_node);
- }
- return exit_code;
-}
-
-static int main0(int argc, char **argv) {
- char *arg0 = argv[0];
- Error err;
-
- if (argc >= 2 && (strcmp(argv[1], "clang") == 0 ||
- strcmp(argv[1], "-cc1") == 0 || strcmp(argv[1], "-cc1as") == 0))
- {
- return ZigClang_main(argc, argv);
- }
-
- if (argc == 2 && strcmp(argv[1], "id") == 0) {
- Buf *compiler_id;
- if ((err = get_compiler_id(&compiler_id))) {
- fprintf(stderr, "Unable to determine compiler id: %s\n", err_str(err));
- return EXIT_FAILURE;
- }
- printf("%s\n", buf_ptr(compiler_id));
- return EXIT_SUCCESS;
- }
-
- enum InitKind {
- InitKindNone,
- InitKindExe,
- InitKindLib,
- };
- InitKind init_kind = InitKindNone;
- if (argc >= 2) {
- const char *init_cmd = argv[1];
- if (strcmp(init_cmd, "init-exe") == 0) {
- init_kind = InitKindExe;
- } else if (strcmp(init_cmd, "init-lib") == 0) {
- init_kind = InitKindLib;
- }
- if (init_kind != InitKindNone) {
- if (argc >= 3) {
- fprintf(stderr, "Unexpected extra argument: %s\n", argv[2]);
- return print_error_usage(arg0);
- }
- Buf *cmd_template_path = buf_alloc();
- os_path_join(get_zig_special_dir(get_zig_lib_dir()), buf_create_from_str(init_cmd), cmd_template_path);
- Buf *build_zig_path = buf_alloc();
- os_path_join(cmd_template_path, buf_create_from_str("build.zig"), build_zig_path);
- Buf *src_dir_path = buf_alloc();
- os_path_join(cmd_template_path, buf_create_from_str("src"), src_dir_path);
- Buf *main_zig_path = buf_alloc();
- os_path_join(src_dir_path, buf_create_from_str("main.zig"), main_zig_path);
-
- Buf *cwd = buf_alloc();
- if ((err = os_get_cwd(cwd))) {
- fprintf(stderr, "Unable to get cwd: %s\n", err_str(err));
- return EXIT_FAILURE;
- }
- Buf *cwd_basename = buf_alloc();
- os_path_split(cwd, nullptr, cwd_basename);
-
- Buf *build_zig_contents = buf_alloc();
- if ((err = os_fetch_file_path(build_zig_path, build_zig_contents))) {
- fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(build_zig_path), err_str(err));
- return EXIT_FAILURE;
- }
- Buf *modified_build_zig_contents = buf_alloc();
- for (size_t i = 0; i < buf_len(build_zig_contents); i += 1) {
- char c = buf_ptr(build_zig_contents)[i];
- if (c == '$') {
- buf_append_buf(modified_build_zig_contents, cwd_basename);
- } else {
- buf_append_char(modified_build_zig_contents, c);
- }
- }
-
- Buf *main_zig_contents = buf_alloc();
- if ((err = os_fetch_file_path(main_zig_path, main_zig_contents))) {
- fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(main_zig_path), err_str(err));
- return EXIT_FAILURE;
- }
-
- Buf *out_build_zig_path = buf_create_from_str("build.zig");
- Buf *out_src_dir_path = buf_create_from_str("src");
- Buf *out_main_zig_path = buf_alloc();
- os_path_join(out_src_dir_path, buf_create_from_str("main.zig"), out_main_zig_path);
-
- bool already_exists;
- if ((err = os_file_exists(out_build_zig_path, &already_exists))) {
- fprintf(stderr, "Unable test existence of %s: %s\n", buf_ptr(out_build_zig_path), err_str(err));
- return EXIT_FAILURE;
- }
- if (already_exists) {
- fprintf(stderr, "This file would be overwritten: %s\n", buf_ptr(out_build_zig_path));
- return EXIT_FAILURE;
- }
-
- if ((err = os_make_dir(out_src_dir_path))) {
- fprintf(stderr, "Unable to make directory: %s: %s\n", buf_ptr(out_src_dir_path), err_str(err));
- return EXIT_FAILURE;
- }
- if ((err = os_write_file(out_build_zig_path, modified_build_zig_contents))) {
- fprintf(stderr, "Unable to write file: %s: %s\n", buf_ptr(out_build_zig_path), err_str(err));
- return EXIT_FAILURE;
- }
- if ((err = os_write_file(out_main_zig_path, main_zig_contents))) {
- fprintf(stderr, "Unable to write file: %s: %s\n", buf_ptr(out_main_zig_path), err_str(err));
- return EXIT_FAILURE;
- }
- fprintf(stderr, "Created %s\n", buf_ptr(out_build_zig_path));
- fprintf(stderr, "Created %s\n", buf_ptr(out_main_zig_path));
- if (init_kind == InitKindExe) {
- fprintf(stderr, "\nNext, try `zig build --help` or `zig build run`\n");
- } else if (init_kind == InitKindLib) {
- fprintf(stderr, "\nNext, try `zig build --help` or `zig build test`\n");
- } else {
- zig_unreachable();
- }
-
- return EXIT_SUCCESS;
- }
- }
-
- Cmd cmd = CmdNone;
- const char *in_file = nullptr;
- Buf *output_dir = nullptr;
- bool strip = false;
- bool is_dynamic = false;
- OutType out_type = OutTypeUnknown;
- const char *out_name = nullptr;
- bool verbose_tokenize = false;
- bool verbose_ast = false;
- bool verbose_link = false;
- bool verbose_ir = false;
- bool verbose_llvm_ir = false;
- bool verbose_cimport = false;
- bool verbose_cc = false;
- bool verbose_llvm_cpu_features = false;
- bool link_eh_frame_hdr = false;
- ErrColor color = ErrColorAuto;
- CacheOpt enable_cache = CacheOptAuto;
- const char *dynamic_linker = nullptr;
- const char *libc_txt = nullptr;
- ZigList clang_argv = {0};
- ZigList lib_dirs = {0};
- ZigList link_libs = {0};
- ZigList forbidden_link_libs = {0};
- ZigList framework_dirs = {0};
- ZigList frameworks = {0};
- bool have_libc = false;
- bool have_libcpp = false;
- const char *target_string = nullptr;
- bool rdynamic = false;
- const char *linker_script = nullptr;
- Buf *version_script = nullptr;
- ZigList rpath_list = {0};
- bool each_lib_rpath = false;
- ZigList objects = {0};
- ZigList c_source_files = {0};
- const char *test_filter = nullptr;
- const char *test_name_prefix = nullptr;
- bool test_evented_io = false;
- bool is_versioned = false;
- size_t ver_major = 0;
- size_t ver_minor = 0;
- size_t ver_patch = 0;
- bool timing_info = false;
- bool stack_report = false;
- bool enable_dump_analysis = false;
- bool enable_doc_generation = false;
- bool emit_bin = true;
- const char *emit_bin_override_path = nullptr;
- bool emit_asm = false;
- bool emit_llvm_ir = false;
- bool emit_h = false;
- const char *cache_dir = nullptr;
- CliPkg *cur_pkg = heap::c_allocator.create();
- BuildMode build_mode = BuildModeDebug;
- ZigList test_exec_args = {0};
- int runtime_args_start = -1;
- bool system_linker_hack = false;
- TargetSubsystem subsystem = TargetSubsystemAuto;
- bool want_single_threaded = false;
- bool bundle_compiler_rt = false;
- Buf *override_lib_dir = nullptr;
- Buf *main_pkg_path = nullptr;
- ValgrindSupport valgrind_support = ValgrindSupportAuto;
- WantPIC want_pic = WantPICAuto;
- WantStackCheck want_stack_check = WantStackCheckAuto;
- WantCSanitize want_sanitize_c = WantCSanitizeAuto;
- bool function_sections = false;
- const char *mcpu = nullptr;
- CodeModel code_model = CodeModelDefault;
- const char *override_soname = nullptr;
- bool only_pp_or_asm = false;
- bool ensure_libc_on_non_freestanding = false;
- bool ensure_libcpp_on_non_freestanding = false;
- bool disable_c_depfile = false;
- bool want_native_include_dirs = false;
- Buf *linker_optimization = nullptr;
- OptionalBool linker_gc_sections = OptionalBoolNull;
- OptionalBool linker_allow_shlib_undefined = OptionalBoolNull;
- OptionalBool linker_bind_global_refs_locally = OptionalBoolNull;
- bool linker_z_nodelete = false;
- bool linker_z_defs = false;
- size_t stack_size_override = 0;
-
- ZigList llvm_argv = {0};
- llvm_argv.append("zig (LLVM option parsing)");
-
- if (argc >= 2 && strcmp(argv[1], "build") == 0) {
- Buf zig_exe_path_buf = BUF_INIT;
- if ((err = os_self_exe_path(&zig_exe_path_buf))) {
- fprintf(stderr, "Unable to determine path to zig's own executable\n");
- return EXIT_FAILURE;
- }
- const char *zig_exe_path = buf_ptr(&zig_exe_path_buf);
- const char *build_file = nullptr;
-
- init_all_targets();
-
- ZigList args = {0};
- args.append(NULL); // placeholder
- args.append(zig_exe_path);
- args.append(NULL); // placeholder
- args.append(NULL); // placeholder
- for (int i = 2; i < argc; i += 1) {
- if (strcmp(argv[i], "--help") == 0) {
- args.append(argv[i]);
- } else if (i + 1 < argc && strcmp(argv[i], "--build-file") == 0) {
- build_file = argv[i + 1];
- i += 1;
- } else if (i + 1 < argc && strcmp(argv[i], "--cache-dir") == 0) {
- cache_dir = argv[i + 1];
- i += 1;
- } else if (i + 1 < argc && strcmp(argv[i], "--override-lib-dir") == 0) {
- override_lib_dir = buf_create_from_str(argv[i + 1]);
- i += 1;
-
- args.append("--override-lib-dir");
- args.append(buf_ptr(override_lib_dir));
- } else {
- args.append(argv[i]);
- }
- }
-
- Buf *zig_lib_dir = (override_lib_dir == nullptr) ? get_zig_lib_dir() : override_lib_dir;
-
- Buf *build_runner_path = buf_alloc();
- os_path_join(get_zig_special_dir(zig_lib_dir), buf_create_from_str("build_runner.zig"), build_runner_path);
-
- ZigTarget target;
- if ((err = target_parse_triple(&target, "native", nullptr, nullptr))) {
- fprintf(stderr, "Unable to get native target: %s\n", err_str(err));
- return EXIT_FAILURE;
- }
-
- Buf *build_file_buf = buf_create_from_str((build_file != nullptr) ? build_file : "build.zig");
- Buf build_file_abs = os_path_resolve(&build_file_buf, 1);
- Buf build_file_basename = BUF_INIT;
- Buf build_file_dirname = BUF_INIT;
- os_path_split(&build_file_abs, &build_file_dirname, &build_file_basename);
-
- for (;;) {
- bool build_file_exists;
- if ((err = os_file_exists(&build_file_abs, &build_file_exists))) {
- fprintf(stderr, "unable to check existence of '%s': %s\n", buf_ptr(&build_file_abs), err_str(err));
- return 1;
- }
- if (build_file_exists)
- break;
-
- if (build_file != nullptr) {
- // they asked for a specific build file path. only look for that one
- return zig_error_no_build_file();
- }
-
- Buf *next_dir = buf_alloc();
- os_path_dirname(&build_file_dirname, next_dir);
- if (buf_eql_buf(&build_file_dirname, next_dir)) {
- // no more parent directories to search, give up
- return zig_error_no_build_file();
- }
- os_path_join(next_dir, &build_file_basename, &build_file_abs);
- buf_init_from_buf(&build_file_dirname, next_dir);
- }
-
- Buf full_cache_dir = BUF_INIT;
- if (cache_dir == nullptr) {
- os_path_join(&build_file_dirname, buf_create_from_str(default_zig_cache_name), &full_cache_dir);
- } else {
- Buf *cache_dir_buf = buf_create_from_str(cache_dir);
- full_cache_dir = os_path_resolve(&cache_dir_buf, 1);
- }
- Stage2ProgressNode *root_progress_node = stage2_progress_start_root(stage2_progress_create(), "", 0, 0);
-
- CodeGen *g = codegen_create(main_pkg_path, build_runner_path, &target, OutTypeExe,
- BuildModeDebug, override_lib_dir, nullptr, &full_cache_dir, false, root_progress_node);
- g->valgrind_support = valgrind_support;
- g->enable_time_report = timing_info;
- codegen_set_out_name(g, buf_create_from_str("build"));
-
- args.items[2] = buf_ptr(&build_file_dirname);
- args.items[3] = buf_ptr(&full_cache_dir);
-
- ZigPackage *build_pkg = codegen_create_package(g, buf_ptr(&build_file_dirname),
- buf_ptr(&build_file_basename), "std.special");
- g->main_pkg->package_table.put(buf_create_from_str("@build"), build_pkg);
- g->enable_cache = get_cache_opt(enable_cache, true);
- codegen_build_and_link(g);
- if (root_progress_node != nullptr) {
- stage2_progress_end(root_progress_node);
- root_progress_node = nullptr;
- }
-
- Termination term;
- args.items[0] = buf_ptr(&g->bin_file_output_path);
- os_spawn_process(args, &term);
- if (term.how != TerminationIdClean || term.code != 0) {
- fprintf(stderr, "\nBuild failed. The following command failed:\n");
- const char *prefix = "";
- for (size_t i = 0; i < args.length; i += 1) {
- fprintf(stderr, "%s%s", prefix, args.at(i));
- prefix = " ";
- }
- fprintf(stderr, "\n");
- }
- return (term.how == TerminationIdClean) ? term.code : -1;
- } else if (argc >= 2 && strcmp(argv[1], "fmt") == 0) {
- return stage2_fmt(argc, argv);
- } else if (argc >= 2 && strcmp(argv[1], "env") == 0) {
- return stage2_env(argc, argv);
- } else if (argc >= 2 && (strcmp(argv[1], "cc") == 0 || strcmp(argv[1], "c++") == 0)) {
- emit_h = false;
- strip = true;
- ensure_libc_on_non_freestanding = true;
- ensure_libcpp_on_non_freestanding = (strcmp(argv[1], "c++") == 0);
- want_native_include_dirs = true;
-
- bool c_arg = false;
- Stage2ClangArgIterator it;
- stage2_clang_arg_iterator(&it, argc, argv);
- bool is_shared_lib = false;
- ZigList linker_args = {};
- while (it.has_next) {
- if ((err = stage2_clang_arg_next(&it))) {
- fprintf(stderr, "unable to parse command line parameters: %s\n", err_str(err));
- return EXIT_FAILURE;
- }
- switch (it.kind) {
- case Stage2ClangArgTarget: // example: -target riscv64-linux-unknown
- target_string = it.only_arg;
- break;
- case Stage2ClangArgO: // -o
- emit_bin_override_path = it.only_arg;
- enable_cache = CacheOptOn;
- break;
- case Stage2ClangArgC: // -c
- c_arg = true;
- break;
- case Stage2ClangArgOther:
- for (size_t i = 0; i < it.other_args_len; i += 1) {
- clang_argv.append(it.other_args_ptr[i]);
- }
- break;
- case Stage2ClangArgPositional: {
- FileExt file_ext = classify_file_ext(it.only_arg, strlen(it.only_arg));
- switch (file_ext) {
- case FileExtAsm:
- case FileExtC:
- case FileExtCpp:
- case FileExtLLVMIr:
- case FileExtLLVMBitCode:
- case FileExtHeader: {
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = it.only_arg;
- c_source_files.append(c_file);
- break;
- }
- case FileExtUnknown:
- objects.append(it.only_arg);
- break;
- }
- break;
- }
- case Stage2ClangArgL: // -l
- if (strcmp(it.only_arg, "c") == 0) {
- have_libc = true;
- link_libs.append("c");
- } else if (strcmp(it.only_arg, "c++") == 0 ||
- strcmp(it.only_arg, "stdc++") == 0)
- {
- have_libcpp = true;
- link_libs.append("c++");
- } else {
- link_libs.append(it.only_arg);
- }
- break;
- case Stage2ClangArgIgnore:
- break;
- case Stage2ClangArgDriverPunt:
- // Never mind what we're doing, just pass the args directly. For example --help.
- return ZigClang_main(argc, argv);
- case Stage2ClangArgPIC:
- want_pic = WantPICEnabled;
- break;
- case Stage2ClangArgNoPIC:
- want_pic = WantPICDisabled;
- break;
- case Stage2ClangArgNoStdLib:
- ensure_libc_on_non_freestanding = false;
- break;
- case Stage2ClangArgNoStdLibCpp:
- ensure_libcpp_on_non_freestanding = false;
- break;
- case Stage2ClangArgShared:
- is_dynamic = true;
- is_shared_lib = true;
- break;
- case Stage2ClangArgRDynamic:
- rdynamic = true;
- break;
- case Stage2ClangArgWL: {
- const char *arg = it.only_arg;
- for (;;) {
- size_t pos = 0;
- while (arg[pos] != ',' && arg[pos] != 0) pos += 1;
- linker_args.append(buf_create_from_mem(arg, pos));
- if (arg[pos] == 0) break;
- arg += pos + 1;
- }
- break;
- }
- case Stage2ClangArgPreprocessOrAsm:
- // this handles both -E and -S
- only_pp_or_asm = true;
- for (size_t i = 0; i < it.other_args_len; i += 1) {
- clang_argv.append(it.other_args_ptr[i]);
- }
- break;
- case Stage2ClangArgOptimize:
- // alright what release mode do they want?
- if (strcmp(it.only_arg, "Os") == 0) {
- build_mode = BuildModeSmallRelease;
- } else if (strcmp(it.only_arg, "O2") == 0 ||
- strcmp(it.only_arg, "O3") == 0 ||
- strcmp(it.only_arg, "O4") == 0)
- {
- build_mode = BuildModeFastRelease;
- } else if (strcmp(it.only_arg, "Og") == 0 ||
- strcmp(it.only_arg, "O0") == 0)
- {
- build_mode = BuildModeDebug;
- } else {
- for (size_t i = 0; i < it.other_args_len; i += 1) {
- clang_argv.append(it.other_args_ptr[i]);
- }
- }
- break;
- case Stage2ClangArgDebug:
- strip = false;
- if (strcmp(it.only_arg, "-g") == 0) {
- // we handled with strip = false above
- } else {
- for (size_t i = 0; i < it.other_args_len; i += 1) {
- clang_argv.append(it.other_args_ptr[i]);
- }
- }
- break;
- case Stage2ClangArgSanitize:
- if (strcmp(it.only_arg, "undefined") == 0) {
- want_sanitize_c = WantCSanitizeEnabled;
- } else {
- for (size_t i = 0; i < it.other_args_len; i += 1) {
- clang_argv.append(it.other_args_ptr[i]);
- }
- }
- break;
- case Stage2ClangArgLinkerScript:
- linker_script = it.only_arg;
- break;
- case Stage2ClangArgVerboseCmds:
- verbose_cc = true;
- verbose_link = true;
- break;
- case Stage2ClangArgForLinker:
- linker_args.append(buf_create_from_str(it.only_arg));
- break;
- case Stage2ClangArgLinkerInputZ:
- linker_args.append(buf_create_from_str("-z"));
- linker_args.append(buf_create_from_str(it.only_arg));
- break;
- case Stage2ClangArgLibDir:
- lib_dirs.append(it.only_arg);
- break;
- case Stage2ClangArgMCpu:
- mcpu = it.only_arg;
- break;
- case Stage2ClangArgDepFile:
- disable_c_depfile = true;
- for (size_t i = 0; i < it.other_args_len; i += 1) {
- clang_argv.append(it.other_args_ptr[i]);
- }
- break;
- case Stage2ClangArgFrameworkDir:
- framework_dirs.append(it.only_arg);
- break;
- case Stage2ClangArgFramework:
- frameworks.append(it.only_arg);
- break;
- case Stage2ClangArgNoStdLibInc:
- want_native_include_dirs = false;
- break;
- }
- }
- // Parse linker args
- for (size_t i = 0; i < linker_args.length; i += 1) {
- Buf *arg = linker_args.at(i);
- if (buf_eql_str(arg, "-soname")) {
- i += 1;
- if (i >= linker_args.length) {
- fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
- return EXIT_FAILURE;
- }
- Buf *soname_buf = linker_args.at(i);
- override_soname = buf_ptr(soname_buf);
- // use it as --name
- // example: libsoundio.so.2
- size_t prefix = 0;
- if (buf_starts_with_str(soname_buf, "lib")) {
- prefix = 3;
- }
- size_t end = buf_len(soname_buf);
- if (buf_ends_with_str(soname_buf, ".so")) {
- end -= 3;
- } else {
- bool found_digit = false;
- while (end > 0 && isdigit(buf_ptr(soname_buf)[end - 1])) {
- found_digit = true;
- end -= 1;
- }
- if (found_digit && end > 0 && buf_ptr(soname_buf)[end - 1] == '.') {
- end -= 1;
- } else {
- end = buf_len(soname_buf);
- }
- if (buf_ends_with_str(buf_slice(soname_buf, prefix, end), ".so")) {
- end -= 3;
- }
- }
- out_name = buf_ptr(buf_slice(soname_buf, prefix, end));
- } else if (buf_eql_str(arg, "-rpath")) {
- i += 1;
- if (i >= linker_args.length) {
- fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
- return EXIT_FAILURE;
- }
- Buf *rpath = linker_args.at(i);
- rpath_list.append(buf_ptr(rpath));
- } else if (buf_eql_str(arg, "-I") ||
- buf_eql_str(arg, "--dynamic-linker") ||
- buf_eql_str(arg, "-dynamic-linker"))
- {
- i += 1;
- if (i >= linker_args.length) {
- fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
- return EXIT_FAILURE;
- }
- dynamic_linker = buf_ptr(linker_args.at(i));
- } else if (buf_eql_str(arg, "-E") ||
- buf_eql_str(arg, "--export-dynamic") ||
- buf_eql_str(arg, "-export-dynamic"))
- {
- rdynamic = true;
- } else if (buf_eql_str(arg, "--version-script")) {
- i += 1;
- if (i >= linker_args.length) {
- fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
- return EXIT_FAILURE;
- }
- version_script = linker_args.at(i);
- } else if (buf_starts_with_str(arg, "-O")) {
- linker_optimization = arg;
- } else if (buf_eql_str(arg, "--gc-sections")) {
- linker_gc_sections = OptionalBoolTrue;
- } else if (buf_eql_str(arg, "--no-gc-sections")) {
- linker_gc_sections = OptionalBoolFalse;
- } else if (buf_eql_str(arg, "--allow-shlib-undefined") ||
- buf_eql_str(arg, "-allow-shlib-undefined"))
- {
- linker_allow_shlib_undefined = OptionalBoolTrue;
- } else if (buf_eql_str(arg, "--no-allow-shlib-undefined") ||
- buf_eql_str(arg, "-no-allow-shlib-undefined"))
- {
- linker_allow_shlib_undefined = OptionalBoolFalse;
- } else if (buf_eql_str(arg, "-Bsymbolic")) {
- linker_bind_global_refs_locally = OptionalBoolTrue;
- } else if (buf_eql_str(arg, "-z")) {
- i += 1;
- if (i >= linker_args.length) {
- fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
- return EXIT_FAILURE;
- }
- Buf *z_arg = linker_args.at(i);
- if (buf_eql_str(z_arg, "nodelete")) {
- linker_z_nodelete = true;
- } else if (buf_eql_str(z_arg, "defs")) {
- linker_z_defs = true;
- } else {
- fprintf(stderr, "warning: unsupported linker arg: -z %s\n", buf_ptr(z_arg));
- }
- } else if (buf_eql_str(arg, "--major-image-version")) {
- i += 1;
- if (i >= linker_args.length) {
- fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
- return EXIT_FAILURE;
- }
- is_versioned = true;
- ver_major = atoi(buf_ptr(linker_args.at(i)));
- } else if (buf_eql_str(arg, "--minor-image-version")) {
- i += 1;
- if (i >= linker_args.length) {
- fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
- return EXIT_FAILURE;
- }
- is_versioned = true;
- ver_minor = atoi(buf_ptr(linker_args.at(i)));
- } else if (buf_eql_str(arg, "--stack")) {
- i += 1;
- if (i >= linker_args.length) {
- fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
- return EXIT_FAILURE;
- }
- stack_size_override = atoi(buf_ptr(linker_args.at(i)));
- } else {
- fprintf(stderr, "warning: unsupported linker arg: %s\n", buf_ptr(arg));
- }
- }
-
- if (want_sanitize_c == WantCSanitizeEnabled && build_mode == BuildModeFastRelease) {
- build_mode = BuildModeSafeRelease;
- }
-
- if (only_pp_or_asm) {
- cmd = CmdBuild;
- out_type = OutTypeObj;
- emit_bin = false;
- // Transfer "objects" into c_source_files
- for (size_t i = 0; i < objects.length; i += 1) {
- CFile *c_file = heap::c_allocator.create();
- c_file->source_path = objects.at(i);
- c_source_files.append(c_file);
- }
- for (size_t i = 0; i < c_source_files.length; i += 1) {
- Buf *src_path;
- if (emit_bin_override_path != nullptr) {
- src_path = buf_create_from_str(emit_bin_override_path);
- } else {
- src_path = buf_create_from_str(c_source_files.at(i)->source_path);
- }
- Buf basename = BUF_INIT;
- os_path_split(src_path, nullptr, &basename);
- c_source_files.at(i)->preprocessor_only_basename = buf_ptr(&basename);
- }
- } else if (!c_arg) {
- cmd = CmdBuild;
- if (is_shared_lib) {
- out_type = OutTypeLib;
- } else {
- out_type = OutTypeExe;
- }
- if (emit_bin_override_path == nullptr) {
- emit_bin_override_path = "a.out";
- enable_cache = CacheOptOn;
- }
- } else {
- cmd = CmdBuild;
- out_type = OutTypeObj;
- }
- if (c_source_files.length == 0 && objects.length == 0) {
- // For example `zig cc` and no args should print the "no input files" message.
- return ZigClang_main(argc, argv);
- }
- } else for (int i = 1; i < argc; i += 1) {
- char *arg = argv[i];
-
- if (arg[0] == '-') {
- if (strcmp(arg, "--") == 0) {
- if (cmd == CmdRun) {
- runtime_args_start = i + 1;
- break; // rest of the args are for the program
- } else {
- fprintf(stderr, "Unexpected end-of-parameter mark: %s\n", arg);
- }
- } else if (strcmp(arg, "--release-fast") == 0) {
- build_mode = BuildModeFastRelease;
- } else if (strcmp(arg, "--release-safe") == 0) {
- build_mode = BuildModeSafeRelease;
- } else if (strcmp(arg, "--release-small") == 0) {
- build_mode = BuildModeSmallRelease;
- } else if (strcmp(arg, "--help") == 0) {
- if (cmd == CmdLibC) {
- return print_libc_usage(arg0, stdout, EXIT_SUCCESS);
- } else {
- return print_full_usage(arg0, stdout, EXIT_SUCCESS);
- }
- } else if (strcmp(arg, "--strip") == 0) {
- strip = true;
- } else if (strcmp(arg, "-dynamic") == 0) {
- is_dynamic = true;
- } else if (strcmp(arg, "--verbose-tokenize") == 0) {
- verbose_tokenize = true;
- } else if (strcmp(arg, "--verbose-ast") == 0) {
- verbose_ast = true;
- } else if (strcmp(arg, "--verbose-link") == 0) {
- verbose_link = true;
- } else if (strcmp(arg, "--verbose-ir") == 0) {
- verbose_ir = true;
- } else if (strcmp(arg, "--verbose-llvm-ir") == 0) {
- verbose_llvm_ir = true;
- } else if (strcmp(arg, "--verbose-cimport") == 0) {
- verbose_cimport = true;
- } else if (strcmp(arg, "--verbose-cc") == 0) {
- verbose_cc = true;
- } else if (strcmp(arg, "--verbose-llvm-cpu-features") == 0) {
- verbose_llvm_cpu_features = true;
- } else if (strcmp(arg, "-rdynamic") == 0) {
- rdynamic = true;
- } else if (strcmp(arg, "--each-lib-rpath") == 0) {
- each_lib_rpath = true;
- } else if (strcmp(arg, "-ftime-report") == 0) {
- timing_info = true;
- } else if (strcmp(arg, "-fstack-report") == 0) {
- stack_report = true;
- } else if (strcmp(arg, "-fmem-report") == 0) {
-#ifdef ZIG_ENABLE_MEM_PROFILE
- mem_report = true;
- mem::report_print = true;
-#else
- fprintf(stderr, "-fmem-report requires configuring with -DZIG_ENABLE_MEM_PROFILE=ON\n");
- return print_error_usage(arg0);
-#endif
- } else if (strcmp(arg, "-fdump-analysis") == 0) {
- enable_dump_analysis = true;
- } else if (strcmp(arg, "-femit-docs") == 0) {
- enable_doc_generation = true;
- } else if (strcmp(arg, "--enable-valgrind") == 0) {
- valgrind_support = ValgrindSupportEnabled;
- } else if (strcmp(arg, "--disable-valgrind") == 0) {
- valgrind_support = ValgrindSupportDisabled;
- } else if (strcmp(arg, "--eh-frame-hdr") == 0) {
- link_eh_frame_hdr = true;
- } else if (strcmp(arg, "-fPIC") == 0) {
- want_pic = WantPICEnabled;
- } else if (strcmp(arg, "-fno-PIC") == 0) {
- want_pic = WantPICDisabled;
- } else if (strcmp(arg, "-fstack-check") == 0) {
- want_stack_check = WantStackCheckEnabled;
- } else if (strcmp(arg, "-fno-stack-check") == 0) {
- want_stack_check = WantStackCheckDisabled;
- } else if (strcmp(arg, "-fsanitize-c") == 0) {
- want_sanitize_c = WantCSanitizeEnabled;
- } else if (strcmp(arg, "-fno-sanitize-c") == 0) {
- want_sanitize_c = WantCSanitizeDisabled;
- } else if (strcmp(arg, "--system-linker-hack") == 0) {
- system_linker_hack = true;
- } else if (strcmp(arg, "--single-threaded") == 0) {
- want_single_threaded = true;;
- } else if (strcmp(arg, "--bundle-compiler-rt") == 0) {
- bundle_compiler_rt = true;
- } else if (strcmp(arg, "-Bsymbolic") == 0) {
- linker_bind_global_refs_locally = OptionalBoolTrue;
- } else if (strcmp(arg, "--test-cmd-bin") == 0) {
- test_exec_args.append(nullptr);
- } else if (arg[1] == 'D' && arg[2] != 0) {
- clang_argv.append("-D");
- clang_argv.append(&arg[2]);
- } else if (arg[1] == 'L' && arg[2] != 0) {
- // alias for --library-path
- lib_dirs.append(&arg[2]);
- } else if (arg[1] == 'l' && arg[2] != 0) {
- // alias for --library
- const char *l = &arg[2];
- if (strcmp(l, "c") == 0) {
- have_libc = true;
- link_libs.append("c");
- } else if (strcmp(l, "c++") == 0 || strcmp(l, "stdc++") == 0) {
- have_libcpp = true;
- link_libs.append("c++");
- } else {
- link_libs.append(l);
- }
- } else if (arg[1] == 'I' && arg[2] != 0) {
- clang_argv.append("-I");
- clang_argv.append(&arg[2]);
- } else if (arg[1] == 'F' && arg[2] != 0) {
- framework_dirs.append(&arg[2]);
- } else if (strcmp(arg, "--pkg-begin") == 0) {
- if (i + 2 >= argc) {
- fprintf(stderr, "Expected 2 arguments after --pkg-begin\n");
- return print_error_usage(arg0);
- }
- CliPkg *new_cur_pkg = heap::c_allocator.create();
- i += 1;
- new_cur_pkg->name = argv[i];
- i += 1;
- new_cur_pkg->path = argv[i];
- new_cur_pkg->parent = cur_pkg;
- cur_pkg->children.append(new_cur_pkg);
- cur_pkg = new_cur_pkg;
- } else if (strcmp(arg, "--pkg-end") == 0) {
- if (cur_pkg->parent == nullptr) {
- fprintf(stderr, "Encountered --pkg-end with no matching --pkg-begin\n");
- return EXIT_FAILURE;
- }
- cur_pkg = cur_pkg->parent;
- } else if (strcmp(arg, "-ffunction-sections") == 0) {
- function_sections = true;
- } else if (strcmp(arg, "--test-evented-io") == 0) {
- test_evented_io = true;
- } else if (strcmp(arg, "-femit-bin") == 0) {
- emit_bin = true;
- } else if (strcmp(arg, "-fno-emit-bin") == 0) {
- emit_bin = false;
- } else if (strcmp(arg, "-femit-asm") == 0) {
- emit_asm = true;
- } else if (strcmp(arg, "-fno-emit-asm") == 0) {
- emit_asm = false;
- } else if (strcmp(arg, "-femit-llvm-ir") == 0) {
- emit_llvm_ir = true;
- } else if (strcmp(arg, "-fno-emit-llvm-ir") == 0) {
- emit_llvm_ir = false;
- } else if (strcmp(arg, "-femit-h") == 0) {
- emit_h = true;
- } else if (strcmp(arg, "-fno-emit-h") == 0 || strcmp(arg, "--disable-gen-h") == 0) {
- // the --disable-gen-h is there to support godbolt. once they upgrade to -fno-emit-h then we can remove this
- emit_h = false;
- } else if (str_starts_with(arg, "-mcpu=")) {
- mcpu = arg + strlen("-mcpu=");
- } else if (i + 1 >= argc) {
- fprintf(stderr, "Expected another argument after %s\n", arg);
- return print_error_usage(arg0);
- } else {
- i += 1;
- if (strcmp(arg, "--output-dir") == 0) {
- output_dir = buf_create_from_str(argv[i]);
- } else if (strcmp(arg, "--color") == 0) {
- if (strcmp(argv[i], "auto") == 0) {
- color = ErrColorAuto;
- } else if (strcmp(argv[i], "on") == 0) {
- color = ErrColorOn;
- } else if (strcmp(argv[i], "off") == 0) {
- color = ErrColorOff;
- } else {
- fprintf(stderr, "--color options are 'auto', 'on', or 'off'\n");
- return print_error_usage(arg0);
- }
- } else if (strcmp(arg, "--cache") == 0) {
- if (strcmp(argv[i], "auto") == 0) {
- enable_cache = CacheOptAuto;
- } else if (strcmp(argv[i], "on") == 0) {
- enable_cache = CacheOptOn;
- } else if (strcmp(argv[i], "off") == 0) {
- enable_cache = CacheOptOff;
- } else {
- fprintf(stderr, "--cache options are 'auto', 'on', or 'off'\n");
- return print_error_usage(arg0);
- }
- } else if (strcmp(arg, "--emit") == 0) {
- if (strcmp(argv[i], "asm") == 0) {
- emit_asm = true;
- emit_bin = false;
- } else if (strcmp(argv[i], "bin") == 0) {
- emit_bin = true;
- } else if (strcmp(argv[i], "llvm-ir") == 0) {
- emit_llvm_ir = true;
- emit_bin = false;
- } else {
- fprintf(stderr, "--emit options are 'asm', 'bin', or 'llvm-ir'\n");
- return print_error_usage(arg0);
- }
- } else if (strcmp(arg, "--name") == 0) {
- out_name = argv[i];
- } else if (strcmp(arg, "--dynamic-linker") == 0) {
- dynamic_linker = argv[i];
- } else if (strcmp(arg, "--libc") == 0) {
- libc_txt = argv[i];
- } else if (strcmp(arg, "-D") == 0) {
- clang_argv.append("-D");
- clang_argv.append(argv[i]);
- } else if (strcmp(arg, "-isystem") == 0) {
- clang_argv.append("-isystem");
- clang_argv.append(argv[i]);
- } else if (strcmp(arg, "-I") == 0) {
- clang_argv.append("-I");
- clang_argv.append(argv[i]);
- } else if (strcmp(arg, "-dirafter") == 0) {
- clang_argv.append("-dirafter");
- clang_argv.append(argv[i]);
- } else if (strcmp(arg, "-mllvm") == 0) {
- clang_argv.append("-mllvm");
- clang_argv.append(argv[i]);
-
- llvm_argv.append(argv[i]);
- } else if (strcmp(arg, "-code-model") == 0) {
- if (strcmp(argv[i], "default") == 0) {
- code_model = CodeModelDefault;
- } else if (strcmp(argv[i], "tiny") == 0) {
- code_model = CodeModelTiny;
- } else if (strcmp(argv[i], "small") == 0) {
- code_model = CodeModelSmall;
- } else if (strcmp(argv[i], "kernel") == 0) {
- code_model = CodeModelKernel;
- } else if (strcmp(argv[i], "medium") == 0) {
- code_model = CodeModelMedium;
- } else if (strcmp(argv[i], "large") == 0) {
- code_model = CodeModelLarge;
- } else {
- fprintf(stderr, "-code-model options are 'default', 'tiny', 'small', 'kernel', 'medium', or 'large'\n");
- return print_error_usage(arg0);
- }
- } else if (strcmp(arg, "--override-lib-dir") == 0) {
- override_lib_dir = buf_create_from_str(argv[i]);
- } else if (strcmp(arg, "--main-pkg-path") == 0) {
- main_pkg_path = buf_create_from_str(argv[i]);
- } else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) {
- lib_dirs.append(argv[i]);
- } else if (strcmp(arg, "-F") == 0) {
- framework_dirs.append(argv[i]);
- } else if (strcmp(arg, "--library") == 0 || strcmp(arg, "-l") == 0) {
- if (strcmp(argv[i], "c") == 0) {
- have_libc = true;
- link_libs.append("c");
- } else if (strcmp(argv[i], "c++") == 0 || strcmp(argv[i], "stdc++") == 0) {
- have_libcpp = true;
- link_libs.append("c++");
- } else {
- link_libs.append(argv[i]);
- }
- } else if (strcmp(arg, "--forbid-library") == 0) {
- forbidden_link_libs.append(argv[i]);
- } else if (strcmp(arg, "--object") == 0) {
- objects.append(argv[i]);
- } else if (strcmp(arg, "--c-source") == 0) {
- CFile *c_file = heap::c_allocator.create();
- for (;;) {
- if (argv[i][0] == '-') {
- c_file->args.append(argv[i]);
- i += 1;
- if (i < argc) {
- continue;
- }
-
- break;
- } else {
- c_file->source_path = argv[i];
- c_source_files.append(c_file);
- break;
- }
- }
- } else if (strcmp(arg, "--cache-dir") == 0) {
- cache_dir = argv[i];
- } else if (strcmp(arg, "-target") == 0) {
- target_string = argv[i];
- } else if (strcmp(arg, "-framework") == 0) {
- frameworks.append(argv[i]);
- } else if (strcmp(arg, "--linker-script") == 0) {
- linker_script = argv[i];
- } else if (strcmp(arg, "--version-script") == 0) {
- version_script = buf_create_from_str(argv[i]);
- } else if (strcmp(arg, "-rpath") == 0) {
- rpath_list.append(argv[i]);
- } else if (strcmp(arg, "--test-filter") == 0) {
- test_filter = argv[i];
- } else if (strcmp(arg, "--test-name-prefix") == 0) {
- test_name_prefix = argv[i];
- } else if (strcmp(arg, "--ver-major") == 0) {
- is_versioned = true;
- ver_major = atoi(argv[i]);
- } else if (strcmp(arg, "--ver-minor") == 0) {
- is_versioned = true;
- ver_minor = atoi(argv[i]);
- } else if (strcmp(arg, "--ver-patch") == 0) {
- is_versioned = true;
- ver_patch = atoi(argv[i]);
- } else if (strcmp(arg, "--test-cmd") == 0) {
- test_exec_args.append(argv[i]);
- } else if (strcmp(arg, "--stack") == 0) {
- stack_size_override = atoi(argv[i]);
- } else if (strcmp(arg, "--subsystem") == 0) {
- if (strcmp(argv[i], "console") == 0) {
- subsystem = TargetSubsystemConsole;
- } else if (strcmp(argv[i], "windows") == 0) {
- subsystem = TargetSubsystemWindows;
- } else if (strcmp(argv[i], "posix") == 0) {
- subsystem = TargetSubsystemPosix;
- } else if (strcmp(argv[i], "native") == 0) {
- subsystem = TargetSubsystemNative;
- } else if (strcmp(argv[i], "efi_application") == 0) {
- subsystem = TargetSubsystemEfiApplication;
- } else if (strcmp(argv[i], "efi_boot_service_driver") == 0) {
- subsystem = TargetSubsystemEfiBootServiceDriver;
- } else if (strcmp(argv[i], "efi_rom") == 0) {
- subsystem = TargetSubsystemEfiRom;
- } else if (strcmp(argv[i], "efi_runtime_driver") == 0) {
- subsystem = TargetSubsystemEfiRuntimeDriver;
- } else {
- fprintf(stderr, "invalid: --subsystem %s\n"
- "Options are:\n"
- " console\n"
- " windows\n"
- " posix\n"
- " native\n"
- " efi_application\n"
- " efi_boot_service_driver\n"
- " efi_rom\n"
- " efi_runtime_driver\n"
- , argv[i]);
- return EXIT_FAILURE;
- }
- } else if (strcmp(arg, "-mcpu") == 0) {
- mcpu = argv[i];
- } else {
- fprintf(stderr, "Invalid argument: %s\n", arg);
- return print_error_usage(arg0);
- }
- }
- } else if (cmd == CmdNone) {
- if (strcmp(arg, "build-exe") == 0) {
- cmd = CmdBuild;
- out_type = OutTypeExe;
- } else if (strcmp(arg, "build-obj") == 0) {
- cmd = CmdBuild;
- out_type = OutTypeObj;
- } else if (strcmp(arg, "build-lib") == 0) {
- cmd = CmdBuild;
- out_type = OutTypeLib;
- } else if (strcmp(arg, "run") == 0) {
- cmd = CmdRun;
- out_type = OutTypeExe;
- } else if (strcmp(arg, "version") == 0) {
- cmd = CmdVersion;
- } else if (strcmp(arg, "zen") == 0) {
- cmd = CmdZen;
- } else if (strcmp(arg, "libc") == 0) {
- cmd = CmdLibC;
- } else if (strcmp(arg, "translate-c") == 0) {
- cmd = CmdTranslateC;
- } else if (strcmp(arg, "test") == 0) {
- cmd = CmdTest;
- out_type = OutTypeExe;
- } else if (strcmp(arg, "targets") == 0) {
- cmd = CmdTargets;
- } else if (strcmp(arg, "builtin") == 0) {
- cmd = CmdBuiltin;
- } else {
- fprintf(stderr, "Unrecognized command: %s\n", arg);
- return print_error_usage(arg0);
- }
- } else {
- switch (cmd) {
- case CmdBuild:
- case CmdRun:
- case CmdTranslateC:
- case CmdTest:
- case CmdLibC:
- if (!in_file) {
- in_file = arg;
- } else {
- fprintf(stderr, "Unexpected extra parameter: %s\n", arg);
- return print_error_usage(arg0);
- }
- break;
- case CmdBuiltin:
- case CmdVersion:
- case CmdZen:
- case CmdTargets:
- fprintf(stderr, "Unexpected extra parameter: %s\n", arg);
- return print_error_usage(arg0);
- case CmdNone:
- zig_unreachable();
- }
- }
- }
-
- if (cur_pkg->parent != nullptr) {
- fprintf(stderr, "Unmatched --pkg-begin\n");
- return EXIT_FAILURE;
- }
-
- Stage2Progress *progress = stage2_progress_create();
- Stage2ProgressNode *root_progress_node = stage2_progress_start_root(progress, "", 0, 0);
- if (color == ErrColorOff) stage2_progress_disable_tty(progress);
-
- init_all_targets();
-
- ZigTarget target;
- if ((err = target_parse_triple(&target, target_string, mcpu, dynamic_linker))) {
- fprintf(stderr, "invalid target: %s\n"
- "See `%s targets` to display valid targets.\n", err_str(err), arg0);
- return print_error_usage(arg0);
- }
-
- if (!have_libc && ensure_libc_on_non_freestanding && target.os != OsFreestanding) {
- have_libc = true;
- link_libs.append("c");
- }
- if (!have_libcpp && ensure_libcpp_on_non_freestanding && target.os != OsFreestanding) {
- have_libcpp = true;
- link_libs.append("c++");
- }
-
- Buf zig_triple_buf = BUF_INIT;
- target_triple_zig(&zig_triple_buf, &target);
-
- // If both output_dir and enable_cache are provided, and doing build-lib, we
- // will just do a file copy at the end. This helps when bootstrapping zig from zig0
- // because we want to pass something like this:
- // zig0 build-lib --cache on --output-dir ${CMAKE_BINARY_DIR}
- // And we don't have access to `zig0 build` because that would require detecting native libc
- // on systems where we are not able to build a libc from source for them.
- // But that's the only reason this works, so otherwise we give an error here.
- Buf *final_output_dir_step = nullptr;
- if (output_dir != nullptr && enable_cache == CacheOptOn) {
- if (cmd == CmdBuild && out_type == OutTypeLib) {
- final_output_dir_step = output_dir;
- output_dir = nullptr;
- } else {
- fprintf(stderr, "`--output-dir` is incompatible with --cache on.\n");
- return print_error_usage(arg0);
- }
- }
-
- if (target_requires_pic(&target, have_libc) && want_pic == WantPICDisabled) {
- fprintf(stderr, "`--disable-pic` is incompatible with target '%s'\n", buf_ptr(&zig_triple_buf));
- return print_error_usage(arg0);
- }
-
- if ((emit_asm || emit_llvm_ir) && in_file == nullptr) {
- fprintf(stderr, "A root source file is required when using `-femit-asm` or `-femit-llvm-ir`\n");
- return print_error_usage(arg0);
- }
-
- if (llvm_argv.length > 1) {
- llvm_argv.append(nullptr);
- ZigLLVMParseCommandLineOptions(llvm_argv.length - 1, llvm_argv.items);
- }
-
- switch (cmd) {
- case CmdLibC: {
- if (in_file) {
- Stage2LibCInstallation libc;
- if ((err = stage2_libc_parse(&libc, in_file))) {
- fprintf(stderr, "unable to parse libc file: %s\n", err_str(err));
- return main_exit(root_progress_node, EXIT_FAILURE);
- }
- return main_exit(root_progress_node, EXIT_SUCCESS);
- }
- Stage2LibCInstallation libc;
- if ((err = stage2_libc_find_native(&libc))) {
- fprintf(stderr, "unable to find native libc file: %s\n", err_str(err));
- return main_exit(root_progress_node, EXIT_FAILURE);
- }
- if ((err = stage2_libc_render(&libc, stdout))) {
- fprintf(stderr, "unable to print libc file: %s\n", err_str(err));
- return main_exit(root_progress_node, EXIT_FAILURE);
- }
- return main_exit(root_progress_node, EXIT_SUCCESS);
- }
- case CmdBuiltin: {
- CodeGen *g = codegen_create(main_pkg_path, nullptr, &target,
- out_type, build_mode, override_lib_dir, nullptr, nullptr, false, root_progress_node);
- codegen_set_strip(g, strip);
- for (size_t i = 0; i < link_libs.length; i += 1) {
- LinkLib *link_lib = codegen_add_link_lib(g, buf_create_from_str(link_libs.at(i)));
- link_lib->provided_explicitly = true;
- }
- g->subsystem = subsystem;
- g->valgrind_support = valgrind_support;
- g->want_pic = want_pic;
- g->want_stack_check = want_stack_check;
- g->want_sanitize_c = want_sanitize_c;
- g->want_single_threaded = want_single_threaded;
- g->test_is_evented = test_evented_io;
- Buf *builtin_source = codegen_generate_builtin_source(g);
- if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {
- fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout)));
- return main_exit(root_progress_node, EXIT_FAILURE);
- }
- return main_exit(root_progress_node, EXIT_SUCCESS);
- }
- case CmdRun:
- case CmdBuild:
- case CmdTranslateC:
- case CmdTest:
- {
- if (cmd == CmdBuild && !in_file && objects.length == 0 &&
- c_source_files.length == 0)
- {
- fprintf(stderr,
- "Expected at least one of these things:\n"
- " * Zig root source file argument\n"
- " * --object argument\n"
- " * --c-source argument\n");
- return print_error_usage(arg0);
- } else if ((cmd == CmdTranslateC ||
- cmd == CmdTest || cmd == CmdRun) && !in_file)
- {
- fprintf(stderr, "Expected source file argument.\n");
- return print_error_usage(arg0);
- } else if (cmd == CmdRun && !emit_bin) {
- fprintf(stderr, "Cannot run without emitting a binary file.\n");
- return print_error_usage(arg0);
- }
-
- bool any_system_lib_dependencies = false;
- for (size_t i = 0; i < link_libs.length; i += 1) {
- if (!target_is_libc_lib_name(&target, link_libs.at(i)) &&
- !target_is_libcpp_lib_name(&target, link_libs.at(i)))
- {
- any_system_lib_dependencies = true;
- break;
- }
- }
-
- if (target.is_native_os && (any_system_lib_dependencies || want_native_include_dirs)) {
- Error err;
- Stage2NativePaths paths;
- if ((err = stage2_detect_native_paths(&paths))) {
- fprintf(stderr, "unable to detect native system paths: %s\n", err_str(err));
- exit(1);
- }
- for (size_t i = 0; i < paths.warnings_len; i += 1) {
- const char *warning = paths.warnings_ptr[i];
- fprintf(stderr, "warning: %s\n", warning);
- }
- for (size_t i = 0; i < paths.include_dirs_len; i += 1) {
- const char *include_dir = paths.include_dirs_ptr[i];
- clang_argv.append("-isystem");
- clang_argv.append(include_dir);
- }
- for (size_t i = 0; i < paths.lib_dirs_len; i += 1) {
- const char *lib_dir = paths.lib_dirs_ptr[i];
- lib_dirs.append(lib_dir);
- }
- for (size_t i = 0; i < paths.rpaths_len; i += 1) {
- const char *rpath = paths.rpaths_ptr[i];
- rpath_list.append(rpath);
- }
- }
-
-
- assert(cmd != CmdBuild || out_type != OutTypeUnknown);
-
- bool need_name = (cmd == CmdBuild || cmd == CmdTranslateC);
-
- if (cmd == CmdRun) {
- out_name = "run";
- }
-
- Buf *in_file_buf = nullptr;
-
- Buf *buf_out_name = (cmd == CmdTest) ? buf_create_from_str("test") :
- (out_name == nullptr) ? nullptr : buf_create_from_str(out_name);
-
- if (in_file) {
- in_file_buf = buf_create_from_str(in_file);
-
- if (need_name && buf_out_name == nullptr) {
- Buf basename = BUF_INIT;
- os_path_split(in_file_buf, nullptr, &basename);
- buf_out_name = buf_alloc();
- os_path_extname(&basename, buf_out_name, nullptr);
- }
- }
-
- if (need_name && buf_out_name == nullptr && c_source_files.length == 1) {
- Buf basename = BUF_INIT;
- os_path_split(buf_create_from_str(c_source_files.at(0)->source_path), nullptr, &basename);
- buf_out_name = buf_alloc();
- os_path_extname(&basename, buf_out_name, nullptr);
- }
- if (need_name && buf_out_name == nullptr && objects.length == 1) {
- Buf basename = BUF_INIT;
- os_path_split(buf_create_from_str(objects.at(0)), nullptr, &basename);
- buf_out_name = buf_alloc();
- os_path_extname(&basename, buf_out_name, nullptr);
- }
- if (need_name && buf_out_name == nullptr && emit_bin_override_path != nullptr) {
- Buf basename = BUF_INIT;
- os_path_split(buf_create_from_str(emit_bin_override_path), nullptr, &basename);
- buf_out_name = buf_alloc();
- os_path_extname(&basename, buf_out_name, nullptr);
- }
-
- if (need_name && buf_out_name == nullptr) {
- fprintf(stderr, "--name [name] not provided and unable to infer\n\n");
- return print_error_usage(arg0);
- }
-
- Buf *zig_root_source_file = (cmd == CmdTranslateC) ? nullptr : in_file_buf;
-
- if (cmd == CmdRun && buf_out_name == nullptr) {
- buf_out_name = buf_create_from_str("run");
- }
- Stage2LibCInstallation *libc = nullptr;
- if (libc_txt != nullptr) {
- libc = heap::c_allocator.create();
- if ((err = stage2_libc_parse(libc, libc_txt))) {
- fprintf(stderr, "Unable to parse --libc text file: %s\n", err_str(err));
- return main_exit(root_progress_node, EXIT_FAILURE);
- }
- }
- Buf *cache_dir_buf;
- if (cache_dir == nullptr) {
- if (cmd == CmdRun) {
- cache_dir_buf = get_global_cache_dir();
- } else {
- cache_dir_buf = buf_create_from_str(default_zig_cache_name);
- }
- } else {
- cache_dir_buf = buf_create_from_str(cache_dir);
- }
- CodeGen *g = codegen_create(main_pkg_path, zig_root_source_file, &target, out_type, build_mode,
- override_lib_dir, libc, cache_dir_buf, cmd == CmdTest, root_progress_node);
- if (llvm_argv.length >= 2) codegen_set_llvm_argv(g, llvm_argv.items + 1, llvm_argv.length - 2);
- g->valgrind_support = valgrind_support;
- g->link_eh_frame_hdr = link_eh_frame_hdr;
- g->want_pic = want_pic;
- g->want_stack_check = want_stack_check;
- g->want_sanitize_c = want_sanitize_c;
- g->subsystem = subsystem;
-
- g->enable_time_report = timing_info;
- g->enable_stack_report = stack_report;
- g->enable_dump_analysis = enable_dump_analysis;
- g->enable_doc_generation = enable_doc_generation;
- g->emit_bin = emit_bin;
- g->emit_asm = emit_asm;
- g->emit_llvm_ir = emit_llvm_ir;
-
- codegen_set_out_name(g, buf_out_name);
- codegen_set_lib_version(g, is_versioned, ver_major, ver_minor, ver_patch);
- g->want_single_threaded = want_single_threaded;
- codegen_set_linker_script(g, linker_script);
- g->version_script_path = version_script;
- if (each_lib_rpath)
- codegen_set_each_lib_rpath(g, each_lib_rpath);
-
- codegen_set_clang_argv(g, clang_argv.items, clang_argv.length);
-
- codegen_set_strip(g, strip);
- g->is_dynamic = is_dynamic;
- g->verbose_tokenize = verbose_tokenize;
- g->verbose_ast = verbose_ast;
- g->verbose_link = verbose_link;
- g->verbose_ir = verbose_ir;
- g->verbose_llvm_ir = verbose_llvm_ir;
- g->verbose_cimport = verbose_cimport;
- g->verbose_cc = verbose_cc;
- g->verbose_llvm_cpu_features = verbose_llvm_cpu_features;
- g->output_dir = output_dir;
- g->disable_gen_h = !emit_h;
- g->bundle_compiler_rt = bundle_compiler_rt;
- codegen_set_errmsg_color(g, color);
- g->system_linker_hack = system_linker_hack;
- g->function_sections = function_sections;
- g->code_model = code_model;
- g->disable_c_depfile = disable_c_depfile;
-
- g->linker_optimization = linker_optimization;
- g->linker_gc_sections = linker_gc_sections;
- g->linker_allow_shlib_undefined = linker_allow_shlib_undefined;
- g->linker_bind_global_refs_locally = linker_bind_global_refs_locally;
- g->linker_z_nodelete = linker_z_nodelete;
- g->linker_z_defs = linker_z_defs;
- g->stack_size_override = stack_size_override;
-
- if (override_soname) {
- g->override_soname = buf_create_from_str(override_soname);
- }
-
- for (size_t i = 0; i < lib_dirs.length; i += 1) {
- codegen_add_lib_dir(g, lib_dirs.at(i));
- }
- for (size_t i = 0; i < framework_dirs.length; i += 1) {
- g->framework_dirs.append(framework_dirs.at(i));
- }
- for (size_t i = 0; i < link_libs.length; i += 1) {
- LinkLib *link_lib = codegen_add_link_lib(g, buf_create_from_str(link_libs.at(i)));
- link_lib->provided_explicitly = true;
- }
- for (size_t i = 0; i < forbidden_link_libs.length; i += 1) {
- Buf *forbidden_link_lib = buf_create_from_str(forbidden_link_libs.at(i));
- codegen_add_forbidden_lib(g, forbidden_link_lib);
- }
- for (size_t i = 0; i < frameworks.length; i += 1) {
- codegen_add_framework(g, frameworks.at(i));
- }
- for (size_t i = 0; i < rpath_list.length; i += 1) {
- codegen_add_rpath(g, rpath_list.at(i));
- }
-
- codegen_set_rdynamic(g, rdynamic);
-
- if (test_filter) {
- codegen_set_test_filter(g, buf_create_from_str(test_filter));
- }
- g->test_is_evented = test_evented_io;
-
- if (test_name_prefix) {
- codegen_set_test_name_prefix(g, buf_create_from_str(test_name_prefix));
- }
-
- add_package(g, cur_pkg, g->main_pkg);
-
- if (cmd == CmdBuild || cmd == CmdRun || cmd == CmdTest) {
- g->c_source_files = c_source_files;
- for (size_t i = 0; i < objects.length; i += 1) {
- codegen_add_object(g, buf_create_from_str(objects.at(i)));
- }
- }
-
-
- if (cmd == CmdBuild || cmd == CmdRun) {
- g->enable_cache = get_cache_opt(enable_cache, cmd == CmdRun);
- codegen_build_and_link(g);
- if (root_progress_node != nullptr) {
- stage2_progress_end(root_progress_node);
- root_progress_node = nullptr;
- }
- if (timing_info)
- codegen_print_timing_report(g, stdout);
- if (stack_report)
- zig_print_stack_report(g, stdout);
-
- if (cmd == CmdRun) {
-#ifdef ZIG_ENABLE_MEM_PROFILE
- if (mem::report_print)
- mem::print_report();
-#endif
-
- const char *exec_path = buf_ptr(&g->bin_file_output_path);
- ZigList args = {0};
-
- args.append(exec_path);
- if (runtime_args_start != -1) {
- for (int i = runtime_args_start; i < argc; ++i) {
- args.append(argv[i]);
- }
- }
- args.append(nullptr);
-
- os_execv(exec_path, args.items);
-
- args.pop();
- Termination term;
- os_spawn_process(args, &term);
- return term.code;
- } else if (cmd == CmdBuild) {
- if (emit_bin_override_path != nullptr) {
-#if defined(ZIG_OS_WINDOWS)
- buf_replace(g->output_dir, '/', '\\');
-#endif
- Buf *dest_path = buf_create_from_str(emit_bin_override_path);
- Buf *source_path;
- if (only_pp_or_asm) {
- source_path = buf_alloc();
- Buf *pp_only_basename = buf_create_from_str(
- c_source_files.at(0)->preprocessor_only_basename);
- os_path_join(g->output_dir, pp_only_basename, source_path);
-
- } else {
- source_path = &g->bin_file_output_path;
- }
- if ((err = os_update_file(source_path, dest_path))) {
- fprintf(stderr, "unable to copy %s to %s: %s\n", buf_ptr(source_path),
- buf_ptr(dest_path), err_str(err));
- return main_exit(root_progress_node, EXIT_FAILURE);
- }
- } else if (only_pp_or_asm) {
-#if defined(ZIG_OS_WINDOWS)
- buf_replace(g->c_artifact_dir, '/', '\\');
-#endif
- // dump the preprocessed output to stdout
- for (size_t i = 0; i < c_source_files.length; i += 1) {
- Buf *source_path = buf_alloc();
- Buf *pp_only_basename = buf_create_from_str(
- c_source_files.at(i)->preprocessor_only_basename);
- os_path_join(g->c_artifact_dir, pp_only_basename, source_path);
- if ((err = os_dump_file(source_path, stdout))) {
- fprintf(stderr, "unable to read %s: %s\n", buf_ptr(source_path),
- err_str(err));
- return main_exit(root_progress_node, EXIT_FAILURE);
- }
- }
- } else if (g->enable_cache) {
-#if defined(ZIG_OS_WINDOWS)
- buf_replace(&g->bin_file_output_path, '/', '\\');
- buf_replace(g->output_dir, '/', '\\');
-#endif
- if (final_output_dir_step != nullptr) {
- Buf *dest_basename = buf_alloc();
- os_path_split(&g->bin_file_output_path, nullptr, dest_basename);
- Buf *dest_path = buf_alloc();
- os_path_join(final_output_dir_step, dest_basename, dest_path);
-
- if ((err = os_update_file(&g->bin_file_output_path, dest_path))) {
- fprintf(stderr, "unable to copy %s to %s: %s\n", buf_ptr(&g->bin_file_output_path),
- buf_ptr(dest_path), err_str(err));
- return main_exit(root_progress_node, EXIT_FAILURE);
- }
- } else {
- if (printf("%s\n", buf_ptr(g->output_dir)) < 0)
- return main_exit(root_progress_node, EXIT_FAILURE);
- }
- }
- return main_exit(root_progress_node, EXIT_SUCCESS);
- } else {
- zig_unreachable();
- }
- } else if (cmd == CmdTranslateC) {
- g->enable_cache = get_cache_opt(enable_cache, false);
- codegen_translate_c(g, in_file_buf);
- if (timing_info)
- codegen_print_timing_report(g, stderr);
- return main_exit(root_progress_node, EXIT_SUCCESS);
- } else if (cmd == CmdTest) {
- ZigTarget native;
- if ((err = target_parse_triple(&native, "native", nullptr, nullptr))) {
- fprintf(stderr, "Unable to get native target: %s\n", err_str(err));
- return EXIT_FAILURE;
- }
-
- g->enable_cache = get_cache_opt(enable_cache, output_dir == nullptr);
- codegen_build_and_link(g);
- if (root_progress_node != nullptr) {
- stage2_progress_end(root_progress_node);
- root_progress_node = nullptr;
- }
-
- if (timing_info) {
- codegen_print_timing_report(g, stdout);
- }
-
- if (stack_report) {
- zig_print_stack_report(g, stdout);
- }
-
- if (!g->emit_bin) {
- fprintf(stderr, "Semantic analysis complete. No binary produced due to -fno-emit-bin.\n");
- return main_exit(root_progress_node, EXIT_SUCCESS);
- }
-
- Buf *test_exe_path_unresolved = &g->bin_file_output_path;
- Buf *test_exe_path = buf_alloc();
- *test_exe_path = os_path_resolve(&test_exe_path_unresolved, 1);
-
- if (!g->emit_bin) {
- fprintf(stderr, "Created %s but skipping execution because no binary generated.\n",
- buf_ptr(test_exe_path));
- return main_exit(root_progress_node, EXIT_SUCCESS);
- }
-
- for (size_t i = 0; i < test_exec_args.length; i += 1) {
- if (test_exec_args.items[i] == nullptr) {
- test_exec_args.items[i] = buf_ptr(test_exe_path);
- }
- }
-
- if (!target_can_exec(&native, &target) && test_exec_args.length == 0) {
- fprintf(stderr, "Created %s but skipping execution because it is non-native.\n",
- buf_ptr(test_exe_path));
- return main_exit(root_progress_node, EXIT_SUCCESS);
- }
-
- Termination term;
- if (test_exec_args.length == 0) {
- test_exec_args.append(buf_ptr(test_exe_path));
- }
- os_spawn_process(test_exec_args, &term);
- if (term.how != TerminationIdClean || term.code != 0) {
- fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n");
- fprintf(stderr, "%s\n", buf_ptr(test_exe_path));
- }
- return main_exit(root_progress_node, (term.how == TerminationIdClean) ? term.code : -1);
- } else {
- zig_unreachable();
- }
- }
- case CmdVersion:
- printf("%s\n", ZIG_VERSION_STRING);
- return main_exit(root_progress_node, EXIT_SUCCESS);
- case CmdZen: {
- const char *ptr;
- size_t len;
- stage2_zen(&ptr, &len);
- fwrite(ptr, len, 1, stdout);
- return main_exit(root_progress_node, EXIT_SUCCESS);
- }
- case CmdTargets:
- return stage2_cmd_targets(target_string, mcpu, dynamic_linker);
- case CmdNone:
- return print_full_usage(arg0, stderr, EXIT_FAILURE);
- }
- zig_unreachable();
-}
-
-int main(int argc, char **argv) {
- stage2_attach_segfault_handler();
- os_init();
- mem::init();
-
- auto result = main0(argc, argv);
-
-#ifdef ZIG_ENABLE_MEM_PROFILE
- if (mem::report_print)
- mem::intern_counters.print_report();
-#endif
- mem::deinit();
- return result;
-}
diff --git a/src/main.zig b/src/main.zig
new file mode 100644
index 0000000000..d421322c17
--- /dev/null
+++ b/src/main.zig
@@ -0,0 +1,3039 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const io = std.io;
+const fs = std.fs;
+const mem = std.mem;
+const process = std.process;
+const Allocator = mem.Allocator;
+const ArrayList = std.ArrayList;
+const ast = std.zig.ast;
+const warn = std.log.warn;
+
+const Compilation = @import("Compilation.zig");
+const link = @import("link.zig");
+const Package = @import("Package.zig");
+const zir = @import("zir.zig");
+const build_options = @import("build_options");
+const introspect = @import("introspect.zig");
+const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
+const translate_c = @import("translate_c.zig");
+const Cache = @import("Cache.zig");
+const target_util = @import("target.zig");
+
+pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
+ std.log.emerg(format, args);
+ process.exit(1);
+}
+
+pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
+
+pub const Color = enum {
+ Auto,
+ Off,
+ On,
+};
+
+const usage =
+ \\Usage: zig [command] [options]
+ \\
+ \\Commands:
+ \\
+ \\ build Build project from build.zig
+ \\ build-exe Create executable from source or object files
+ \\ build-lib Create library from source or object files
+ \\ build-obj Create object from source or assembly
+ \\ cc Use Zig as a drop-in C compiler
+ \\ c++ Use Zig as a drop-in C++ compiler
+ \\ env Print lib path, std path, compiler id and version
+ \\ fmt Parse file and render in canonical zig format
+ \\ init-exe Initialize a `zig build` application in the cwd
+ \\ init-lib Initialize a `zig build` library in the cwd
+ \\ libc Display native libc paths file or validate one
+ \\ run Create executable and run immediately
+ \\ translate-c Convert C code to Zig code
+ \\ targets List available compilation targets
+ \\ test Create and run a test build
+ \\ version Print version number and exit
+ \\ zen Print zen of zig and exit
+ \\
+ \\General Options:
+ \\
+ \\ --help Print command-specific usage
+ \\
+;
+
+pub const log_level: std.log.Level = switch (std.builtin.mode) {
+ .Debug => .debug,
+ .ReleaseSafe, .ReleaseFast => .info,
+ .ReleaseSmall => .crit,
+};
+
+pub fn log(
+ comptime level: std.log.Level,
+ comptime scope: @TypeOf(.EnumLiteral),
+ comptime format: []const u8,
+ args: anytype,
+) void {
+ // Hide debug messages unless added with `-Dlog=foo`.
+ if (@enumToInt(level) > @enumToInt(std.log.level) or
+ @enumToInt(level) > @enumToInt(std.log.Level.info))
+ {
+ const scope_name = @tagName(scope);
+ const ok = comptime for (build_options.log_scopes) |log_scope| {
+ if (mem.eql(u8, log_scope, scope_name))
+ break true;
+ } else return;
+ }
+
+ // We only recognize 4 log levels in this application.
+ const level_txt = switch (level) {
+ .emerg, .alert, .crit, .err => "error",
+ .warn => "warning",
+ .notice, .info => "info",
+ .debug => "debug",
+ };
+ const prefix1 = level_txt;
+ const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
+
+ // Print the message to stderr, silently ignoring any errors
+ std.debug.print(prefix1 ++ prefix2 ++ format ++ "\n", args);
+}
+
+var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
+
+pub fn main() anyerror!void {
+ const gpa = if (std.builtin.link_libc) std.heap.c_allocator else &general_purpose_allocator.allocator;
+ defer if (!std.builtin.link_libc) {
+ _ = general_purpose_allocator.deinit();
+ };
+ var arena_instance = std.heap.ArenaAllocator.init(gpa);
+ defer arena_instance.deinit();
+ const arena = &arena_instance.allocator;
+
+ const args = try process.argsAlloc(arena);
+ return mainArgs(gpa, arena, args);
+}
+
+pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !void {
+ if (args.len <= 1) {
+ std.log.info("{}", .{usage});
+ fatal("expected command argument", .{});
+ }
+
+ const cmd = args[1];
+ const cmd_args = args[2..];
+ if (mem.eql(u8, cmd, "build-exe")) {
+ return buildOutputType(gpa, arena, args, .{ .build = .Exe });
+ } else if (mem.eql(u8, cmd, "build-lib")) {
+ return buildOutputType(gpa, arena, args, .{ .build = .Lib });
+ } else if (mem.eql(u8, cmd, "build-obj")) {
+ return buildOutputType(gpa, arena, args, .{ .build = .Obj });
+ } else if (mem.eql(u8, cmd, "test")) {
+ return buildOutputType(gpa, arena, args, .zig_test);
+ } else if (mem.eql(u8, cmd, "run")) {
+ return buildOutputType(gpa, arena, args, .run);
+ } else if (mem.eql(u8, cmd, "cc")) {
+ return buildOutputType(gpa, arena, args, .cc);
+ } else if (mem.eql(u8, cmd, "c++")) {
+ return buildOutputType(gpa, arena, args, .cpp);
+ } else if (mem.eql(u8, cmd, "translate-c")) {
+ return buildOutputType(gpa, arena, args, .translate_c);
+ } else if (mem.eql(u8, cmd, "clang") or
+ mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))
+ {
+ return punt_to_clang(arena, args);
+ } else if (mem.eql(u8, cmd, "build")) {
+ return cmdBuild(gpa, arena, cmd_args);
+ } else if (mem.eql(u8, cmd, "fmt")) {
+ return cmdFmt(gpa, cmd_args);
+ } else if (mem.eql(u8, cmd, "libc")) {
+ return cmdLibC(gpa, cmd_args);
+ } else if (mem.eql(u8, cmd, "init-exe")) {
+ return cmdInit(gpa, arena, cmd_args, .Exe);
+ } else if (mem.eql(u8, cmd, "init-lib")) {
+ return cmdInit(gpa, arena, cmd_args, .Lib);
+ } else if (mem.eql(u8, cmd, "targets")) {
+ const info = try detectNativeTargetInfo(arena, .{});
+ const stdout = io.getStdOut().outStream();
+ return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
+ } else if (mem.eql(u8, cmd, "version")) {
+ try std.io.getStdOut().writeAll(build_options.version ++ "\n");
+ } else if (mem.eql(u8, cmd, "env")) {
+ try @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().outStream());
+ } else if (mem.eql(u8, cmd, "zen")) {
+ try io.getStdOut().writeAll(info_zen);
+ } else if (mem.eql(u8, cmd, "help")) {
+ try io.getStdOut().writeAll(usage);
+ } else {
+ std.log.info("{}", .{usage});
+ fatal("unknown command: {}", .{args[1]});
+ }
+}
+
+const usage_build_generic =
+ \\Usage: zig build-exe [files]
+ \\ zig build-lib [files]
+ \\ zig build-obj [files]
+ \\ zig test [files]
+ \\ zig run [file] [-- [args]]
+ \\
+ \\Supported file types:
+ \\ .zig Zig source code
+ \\ .zir Zig Intermediate Representation code
+ \\ .o ELF object file
+ \\ .o MACH-O (macOS) object file
+ \\ .obj COFF (Windows) object file
+ \\ .lib COFF (Windows) static library
+ \\ .a ELF static library
+ \\ .so ELF shared object (dynamic link)
+ \\ .dll Windows Dynamic Link Library
+ \\ .dylib MACH-O (macOS) dynamic library
+ \\ .s Target-specific assembly source code
+ \\ .S Assembly with C preprocessor (requires LLVM extensions)
+ \\ .c C source code (requires LLVM extensions)
+ \\ .cpp C++ source code (requires LLVM extensions)
+ \\ Other C++ extensions: .C .cc .cxx
+ \\
+ \\General Options:
+ \\ -h, --help Print this help and exit
+ \\ --watch Enable compiler REPL
+ \\ --color [auto|off|on] Enable or disable colored error messages
+ \\ -femit-bin[=path] (default) Output machine code
+ \\ -fno-emit-bin Do not output machine code
+ \\ -femit-asm[=path] Output .s (assembly code)
+ \\ -fno-emit-asm (default) Do not output .s (assembly code)
+ \\ -femit-zir[=path] Produce a .zir file with Zig IR
+ \\ -fno-emit-zir (default) Do not produce a .zir file with Zig IR
+ \\ -femit-llvm-ir[=path] Produce a .ll file with LLVM IR (requires LLVM extensions)
+ \\ -fno-emit-llvm-ir (default) Do not produce a .ll file with LLVM IR
+ \\ -femit-h[=path] Generate a C header file (.h)
+ \\ -fno-emit-h (default) Do not generate a C header file (.h)
+ \\ -femit-docs[=path] Create a docs/ dir with html documentation
+ \\ -fno-emit-docs (default) Do not produce docs/ dir with html documentation
+ \\ -femit-analysis[=path] Write analysis JSON file with type information
+ \\ -fno-emit-analysis (default) Do not write analysis JSON file with type information
+ \\ --show-builtin Output the source of @import("builtin") then exit
+ \\ --cache-dir [path] Override the local cache directory
+ \\ --global-cache-dir [path] Override the global cache directory
+ \\ --override-lib-dir [path] Override path to Zig installation lib directory
+ \\ --enable-cache Output to cache directory; print path to stdout
+ \\
+ \\Compile Options:
+ \\ -target [name] -- see the targets command
+ \\ -mcpu [cpu] Specify target CPU and feature set
+ \\ -mcmodel=[default|tiny| Limit range of code and data virtual addresses
+ \\ small|kernel|
+ \\ medium|large]
+ \\ --name [name] Override root name (not a file path)
+ \\ -O [mode] Choose what to optimize for
+ \\ Debug (default) Optimizations off, safety on
+ \\ ReleaseFast Optimizations on, safety off
+ \\ ReleaseSafe Optimizations on, safety on
+ \\ ReleaseSmall Optimize for small binary, safety off
+ \\ --pkg-begin [name] [path] Make pkg available to import and push current pkg
+ \\ --pkg-end Pop current pkg
+ \\ --main-pkg-path Set the directory of the root package
+ \\ -fPIC Force-enable Position Independent Code
+ \\ -fno-PIC Force-disable Position Independent Code
+ \\ -fstack-check Enable stack probing in unsafe builds
+ \\ -fno-stack-check Disable stack probing in safe builds
+ \\ -fsanitize-c Enable C undefined behavior detection in unsafe builds
+ \\ -fno-sanitize-c Disable C undefined behavior detection in safe builds
+ \\ -fvalgrind Include valgrind client requests in release builds
+ \\ -fno-valgrind Omit valgrind client requests in debug builds
+ \\ -fdll-export-fns Mark exported functions as DLL exports (Windows)
+ \\ -fno-dll-export-fns Force-disable marking exported functions as DLL exports
+ \\ --strip Omit debug symbols
+ \\ --single-threaded Code assumes it is only used single-threaded
+ \\ -ofmt=[mode] Override target object format
+ \\ elf Executable and Linking Format
+ \\ c Compile to C source code
+ \\ wasm WebAssembly
+ \\ pe Portable Executable (Windows)
+ \\ coff Common Object File Format (Windows)
+ \\ macho macOS relocatables
+ \\ hex (planned) Intel IHEX
+ \\ raw (planned) Dump machine code directly
+ \\ -dirafter [dir] Add directory to AFTER include search path
+ \\ -isystem [dir] Add directory to SYSTEM include search path
+ \\ -I[dir] Add directory to include search path
+ \\ -D[macro]=[value] Define C [macro] to [value] (1 if [value] omitted)
+ \\ --libc [file] Provide a file which specifies libc paths
+ \\ -cflags [flags] -- Set extra flags for the next positional C source files
+ \\ -ffunction-sections Places each function in a separate section
+ \\
+ \\Link Options:
+ \\ -l[lib], --library [lib] Link against system library
+ \\ -L[d], --library-directory [d] Add a directory to the library search path
+ \\ -T[script], --script [script] Use a custom linker script
+ \\ --version-script [path] Provide a version .map file
+ \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
+ \\ --each-lib-rpath Add rpath for each used dynamic library
+ \\ --version [ver] Dynamic library semver
+ \\ -rdynamic Add all symbols to the dynamic symbol table
+ \\ -rpath [path] Add directory to the runtime library search path
+ \\ --eh-frame-hdr Enable C++ exception handling by passing --eh-frame-hdr to linker
+ \\ -dynamic Force output to be dynamically linked
+ \\ -static Force output to be statically linked
+ \\ -Bsymbolic Bind global references locally
+ \\ --subsystem [subsystem] (windows) /SUBSYSTEM: to the linker\n"
+ \\ --stack [size] Override default stack size
+ \\ -framework [name] (darwin) link against framework
+ \\ -F[dir] (darwin) add search path for frameworks
+ \\
+ \\Test Options:
+ \\ --test-filter [text] Skip tests that do not match filter
+ \\ --test-name-prefix [text] Add prefix to all tests
+ \\ --test-cmd [arg] Specify test execution command one arg at a time
+ \\ --test-cmd-bin Appends test binary path to test cmd args
+ \\ --test-evented-io Runs the test in evented I/O mode
+ \\
+ \\Debug Options (Zig Compiler Development):
+ \\ -ftime-report Print timing diagnostics
+ \\ -fstack-report Print stack size diagnostics
+ \\ --verbose-link Display linker invocations
+ \\ --verbose-cc Display C compiler invocations
+ \\ --verbose-tokenize Enable compiler debug output for tokenization
+ \\ --verbose-ast Enable compiler debug output for AST parsing
+ \\ --verbose-ir Enable compiler debug output for Zig IR
+ \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR
+ \\ --verbose-cimport Enable compiler debug output for C imports
+ \\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features
+ \\
+;
+
+const repl_help =
+ \\Commands:
+ \\ update Detect changes to source files and update output files.
+ \\ help Print this text
+ \\ exit Quit this repl
+ \\
+;
+
+const Emit = union(enum) {
+ no,
+ yes_default_path,
+ yes: []const u8,
+
+ const Resolved = struct {
+ data: ?Compilation.EmitLoc,
+ dir: ?fs.Dir,
+
+ fn deinit(self: *Resolved) void {
+ if (self.dir) |*dir| {
+ dir.close();
+ }
+ }
+ };
+
+ fn resolve(emit: Emit, default_basename: []const u8) !Resolved {
+ var resolved: Resolved = .{ .data = null, .dir = null };
+ errdefer resolved.deinit();
+
+ switch (emit) {
+ .no => {},
+ .yes_default_path => {
+ resolved.data = Compilation.EmitLoc{
+ .directory = .{ .path = null, .handle = fs.cwd() },
+ .basename = default_basename,
+ };
+ },
+ .yes => |full_path| {
+ const basename = fs.path.basename(full_path);
+ if (fs.path.dirname(full_path)) |dirname| {
+ const handle = try fs.cwd().openDir(dirname, .{});
+ resolved = .{
+ .dir = handle,
+ .data = Compilation.EmitLoc{
+ .basename = basename,
+ .directory = .{
+ .path = dirname,
+ .handle = handle,
+ },
+ },
+ };
+ } else {
+ resolved.data = Compilation.EmitLoc{
+ .basename = basename,
+ .directory = .{ .path = null, .handle = fs.cwd() },
+ };
+ }
+ },
+ }
+ return resolved;
+ }
+};
+
+fn buildOutputType(
+ gpa: *Allocator,
+ arena: *Allocator,
+ all_args: []const []const u8,
+ arg_mode: union(enum) {
+ build: std.builtin.OutputMode,
+ cc,
+ cpp,
+ translate_c,
+ zig_test,
+ run,
+ },
+) !void {
+ var color: Color = .Auto;
+ var optimize_mode: std.builtin.Mode = .Debug;
+ var provided_name: ?[]const u8 = null;
+ var link_mode: ?std.builtin.LinkMode = null;
+ var dll_export_fns: ?bool = null;
+ var root_src_file: ?[]const u8 = null;
+ var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 };
+ var have_version = false;
+ var strip = false;
+ var single_threaded = false;
+ var function_sections = false;
+ var watch = false;
+ var verbose_link = false;
+ var verbose_cc = false;
+ var verbose_tokenize = false;
+ var verbose_ast = false;
+ var verbose_ir = false;
+ var verbose_llvm_ir = false;
+ var verbose_cimport = false;
+ var verbose_llvm_cpu_features = false;
+ var time_report = false;
+ var stack_report = false;
+ var show_builtin = false;
+ var emit_bin: Emit = .yes_default_path;
+ var emit_asm: Emit = .no;
+ var emit_llvm_ir: Emit = .no;
+ var emit_zir: Emit = .no;
+ var emit_docs: Emit = .no;
+ var emit_analysis: Emit = .no;
+ var target_arch_os_abi: []const u8 = "native";
+ var target_mcpu: ?[]const u8 = null;
+ var target_dynamic_linker: ?[]const u8 = null;
+ var target_ofmt: ?[]const u8 = null;
+ var output_mode: std.builtin.OutputMode = undefined;
+ var emit_h: Emit = undefined;
+ var ensure_libc_on_non_freestanding = false;
+ var ensure_libcpp_on_non_freestanding = false;
+ var link_libc = false;
+ var link_libcpp = false;
+ var want_native_include_dirs = false;
+ var enable_cache: ?bool = null;
+ var want_pic: ?bool = null;
+ var want_sanitize_c: ?bool = null;
+ var want_stack_check: ?bool = null;
+ var want_valgrind: ?bool = null;
+ var rdynamic: bool = false;
+ var linker_script: ?[]const u8 = null;
+ var version_script: ?[]const u8 = null;
+ var disable_c_depfile = false;
+ var override_soname: ?[]const u8 = null;
+ var linker_gc_sections: ?bool = null;
+ var linker_allow_shlib_undefined: ?bool = null;
+ var linker_bind_global_refs_locally: ?bool = null;
+ var linker_z_nodelete = false;
+ var linker_z_defs = false;
+ var test_evented_io = false;
+ var stack_size_override: ?u64 = null;
+ var use_llvm: ?bool = null;
+ var use_lld: ?bool = null;
+ var use_clang: ?bool = null;
+ var link_eh_frame_hdr = false;
+ var each_lib_rpath = false;
+ var libc_paths_file: ?[]const u8 = null;
+ var machine_code_model: std.builtin.CodeModel = .default;
+ var runtime_args_start: ?usize = null;
+ var test_filter: ?[]const u8 = null;
+ var test_name_prefix: ?[]const u8 = null;
+ var override_local_cache_dir: ?[]const u8 = null;
+ var override_global_cache_dir: ?[]const u8 = null;
+ var override_lib_dir: ?[]const u8 = null;
+ var main_pkg_path: ?[]const u8 = null;
+ var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no;
+ var subsystem: ?std.Target.SubSystem = null;
+
+ var system_libs = std.ArrayList([]const u8).init(gpa);
+ defer system_libs.deinit();
+
+ var clang_argv = std.ArrayList([]const u8).init(gpa);
+ defer clang_argv.deinit();
+
+ var extra_cflags = std.ArrayList([]const u8).init(gpa);
+ defer extra_cflags.deinit();
+
+ var lld_argv = std.ArrayList([]const u8).init(gpa);
+ defer lld_argv.deinit();
+
+ var lib_dirs = std.ArrayList([]const u8).init(gpa);
+ defer lib_dirs.deinit();
+
+ var rpath_list = std.ArrayList([]const u8).init(gpa);
+ defer rpath_list.deinit();
+
+ var c_source_files = std.ArrayList(Compilation.CSourceFile).init(gpa);
+ defer c_source_files.deinit();
+
+ var link_objects = std.ArrayList([]const u8).init(gpa);
+ defer link_objects.deinit();
+
+ var framework_dirs = std.ArrayList([]const u8).init(gpa);
+ defer framework_dirs.deinit();
+
+ var frameworks = std.ArrayList([]const u8).init(gpa);
+ defer frameworks.deinit();
+
+ // null means replace with the test executable binary
+ var test_exec_args = std.ArrayList(?[]const u8).init(gpa);
+ defer test_exec_args.deinit();
+
+ var root_pkg_memory: Package = .{
+ .root_src_directory = undefined,
+ .root_src_path = undefined,
+ };
+ defer root_pkg_memory.table.deinit(gpa);
+ var cur_pkg: *Package = &root_pkg_memory;
+
+ switch (arg_mode) {
+ .build, .translate_c, .zig_test, .run => {
+ var optimize_mode_string: ?[]const u8 = null;
+ switch (arg_mode) {
+ .build => |m| {
+ output_mode = m;
+ },
+ .translate_c => {
+ emit_bin = .no;
+ output_mode = .Obj;
+ },
+ .zig_test, .run => {
+ output_mode = .Exe;
+ },
+ else => unreachable,
+ }
+ // TODO finish self-hosted and add support for emitting C header files
+ emit_h = .no;
+ //switch (arg_mode) {
+ // .build => switch (output_mode) {
+ // .Exe => emit_h = .no,
+ // .Obj, .Lib => emit_h = .yes_default_path,
+ // },
+ // .translate_c, .zig_test, .run => emit_h = .no,
+ // else => unreachable,
+ //}
+ const args = all_args[2..];
+ var i: usize = 0;
+ while (i < args.len) : (i += 1) {
+ const arg = args[i];
+ if (mem.startsWith(u8, arg, "-")) {
+ if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
+ try io.getStdOut().writeAll(usage_build_generic);
+ return cleanExit();
+ } else if (mem.eql(u8, arg, "--")) {
+ if (arg_mode == .run) {
+ runtime_args_start = i + 1;
+ } else {
+ fatal("unexpected end-of-parameter mark: --", .{});
+ }
+ } else if (mem.eql(u8, arg, "--pkg-begin")) {
+ if (i + 2 >= args.len) fatal("Expected 2 arguments after {}", .{arg});
+ i += 1;
+ const pkg_name = args[i];
+ i += 1;
+ const pkg_path = args[i];
+
+ const new_cur_pkg = try arena.create(Package);
+ new_cur_pkg.* = .{
+ .root_src_directory = if (fs.path.dirname(pkg_path)) |dirname|
+ .{
+ .path = dirname,
+ .handle = try fs.cwd().openDir(dirname, .{}), // TODO close this fd
+ }
+ else
+ .{
+ .path = null,
+ .handle = fs.cwd(),
+ },
+ .root_src_path = fs.path.basename(pkg_path),
+ .parent = cur_pkg,
+ };
+ try cur_pkg.table.put(gpa, pkg_name, new_cur_pkg);
+ cur_pkg = new_cur_pkg;
+ } else if (mem.eql(u8, arg, "--pkg-end")) {
+ cur_pkg = cur_pkg.parent orelse
+ fatal("encountered --pkg-end with no matching --pkg-begin", .{});
+ } else if (mem.eql(u8, arg, "--main-pkg-path")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ main_pkg_path = args[i];
+ } else if (mem.eql(u8, arg, "-cflags")) {
+ extra_cflags.shrinkRetainingCapacity(0);
+ while (true) {
+ i += 1;
+ if (i + 1 >= args.len) fatal("expected -- after -cflags", .{});
+ if (mem.eql(u8, args[i], "--")) break;
+ try extra_cflags.append(args[i]);
+ }
+ } else if (mem.eql(u8, arg, "--color")) {
+ if (i + 1 >= args.len) {
+ fatal("expected [auto|on|off] after --color", .{});
+ }
+ i += 1;
+ const next_arg = args[i];
+ if (mem.eql(u8, next_arg, "auto")) {
+ color = .Auto;
+ } else if (mem.eql(u8, next_arg, "on")) {
+ color = .On;
+ } else if (mem.eql(u8, next_arg, "off")) {
+ color = .Off;
+ } else {
+ fatal("expected [auto|on|off] after --color, found '{}'", .{next_arg});
+ }
+ } else if (mem.eql(u8, arg, "--subsystem")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ if (mem.eql(u8, args[i], "console")) {
+ subsystem = .Console;
+ } else if (mem.eql(u8, args[i], "windows")) {
+ subsystem = .Windows;
+ } else if (mem.eql(u8, args[i], "posix")) {
+ subsystem = .Posix;
+ } else if (mem.eql(u8, args[i], "native")) {
+ subsystem = .Native;
+ } else if (mem.eql(u8, args[i], "efi_application")) {
+ subsystem = .EfiApplication;
+ } else if (mem.eql(u8, args[i], "efi_boot_service_driver")) {
+ subsystem = .EfiBootServiceDriver;
+ } else if (mem.eql(u8, args[i], "efi_rom")) {
+ subsystem = .EfiRom;
+ } else if (mem.eql(u8, args[i], "efi_runtime_driver")) {
+ subsystem = .EfiRuntimeDriver;
+ } else {
+ fatal("invalid: --subsystem: '{s}'. Options are:\n{s}", .{
+ args[i],
+ \\ console
+ \\ windows
+ \\ posix
+ \\ native
+ \\ efi_application
+ \\ efi_boot_service_driver
+ \\ efi_rom
+ \\ efi_runtime_driver
+ \\
+ });
+ }
+ } else if (mem.eql(u8, arg, "-O")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ optimize_mode_string = args[i];
+ } else if (mem.eql(u8, arg, "--stack")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ stack_size_override = std.fmt.parseInt(u64, args[i], 10) catch |err| {
+ fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
+ };
+ } else if (mem.eql(u8, arg, "--name")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ provided_name = args[i];
+ } else if (mem.eql(u8, arg, "-rpath")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ try rpath_list.append(args[i]);
+ } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ try lib_dirs.append(args[i]);
+ } else if (mem.eql(u8, arg, "-F")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ try framework_dirs.append(args[i]);
+ } else if (mem.eql(u8, arg, "-framework")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ try frameworks.append(args[i]);
+ } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ linker_script = args[i];
+ } else if (mem.eql(u8, arg, "--version-script")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ version_script = args[i];
+ } else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ // We don't know whether this library is part of libc or libc++ until we resolve the target.
+ // So we simply append to the list for now.
+ i += 1;
+ try system_libs.append(args[i]);
+ } else if (mem.eql(u8, arg, "-D") or
+ mem.eql(u8, arg, "-isystem") or
+ mem.eql(u8, arg, "-I") or
+ mem.eql(u8, arg, "-dirafter"))
+ {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ try clang_argv.append(arg);
+ try clang_argv.append(args[i]);
+ } else if (mem.eql(u8, arg, "--version")) {
+ if (i + 1 >= args.len) {
+ fatal("expected parameter after --version", .{});
+ }
+ i += 1;
+ version = std.builtin.Version.parse(args[i]) catch |err| {
+ fatal("unable to parse --version '{}': {}", .{ args[i], @errorName(err) });
+ };
+ have_version = true;
+ } else if (mem.eql(u8, arg, "-target")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ target_arch_os_abi = args[i];
+ } else if (mem.eql(u8, arg, "-mcpu")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ target_mcpu = args[i];
+ } else if (mem.eql(u8, arg, "-mcmodel")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ machine_code_model = parseCodeModel(args[i]);
+ } else if (mem.startsWith(u8, arg, "-ofmt=")) {
+ target_ofmt = arg["-ofmt=".len..];
+ } else if (mem.startsWith(u8, arg, "-mcpu=")) {
+ target_mcpu = arg["-mcpu=".len..];
+ } else if (mem.startsWith(u8, arg, "-mcmodel=")) {
+ machine_code_model = parseCodeModel(arg["-mcmodel=".len..]);
+ } else if (mem.startsWith(u8, arg, "-O")) {
+ optimize_mode_string = arg["-O".len..];
+ } else if (mem.eql(u8, arg, "--dynamic-linker")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ target_dynamic_linker = args[i];
+ } else if (mem.eql(u8, arg, "--libc")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ libc_paths_file = args[i];
+ } else if (mem.eql(u8, arg, "--test-filter")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ test_filter = args[i];
+ } else if (mem.eql(u8, arg, "--test-name-prefix")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ test_name_prefix = args[i];
+ } else if (mem.eql(u8, arg, "--test-cmd")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ try test_exec_args.append(args[i]);
+ } else if (mem.eql(u8, arg, "--cache-dir")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ override_local_cache_dir = args[i];
+ } else if (mem.eql(u8, arg, "--global-cache-dir")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ override_global_cache_dir = args[i];
+ } else if (mem.eql(u8, arg, "--override-lib-dir")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ override_lib_dir = args[i];
+ } else if (mem.eql(u8, arg, "--each-lib-rpath")) {
+ each_lib_rpath = true;
+ } else if (mem.eql(u8, arg, "--enable-cache")) {
+ enable_cache = true;
+ } else if (mem.eql(u8, arg, "--test-cmd-bin")) {
+ try test_exec_args.append(null);
+ } else if (mem.eql(u8, arg, "--test-evented-io")) {
+ test_evented_io = true;
+ } else if (mem.eql(u8, arg, "--watch")) {
+ watch = true;
+ } else if (mem.eql(u8, arg, "-ftime-report")) {
+ time_report = true;
+ } else if (mem.eql(u8, arg, "-fstack-report")) {
+ stack_report = true;
+ } else if (mem.eql(u8, arg, "-fPIC")) {
+ want_pic = true;
+ } else if (mem.eql(u8, arg, "-fno-PIC")) {
+ want_pic = false;
+ } else if (mem.eql(u8, arg, "-fstack-check")) {
+ want_stack_check = true;
+ } else if (mem.eql(u8, arg, "-fno-stack-check")) {
+ want_stack_check = false;
+ } else if (mem.eql(u8, arg, "-fsanitize-c")) {
+ want_sanitize_c = true;
+ } else if (mem.eql(u8, arg, "-fno-sanitize-c")) {
+ want_sanitize_c = false;
+ } else if (mem.eql(u8, arg, "-fvalgrind")) {
+ want_valgrind = true;
+ } else if (mem.eql(u8, arg, "-fno-valgrind")) {
+ want_valgrind = false;
+ } else if (mem.eql(u8, arg, "-fLLVM")) {
+ use_llvm = true;
+ } else if (mem.eql(u8, arg, "-fno-LLVM")) {
+ use_llvm = false;
+ } else if (mem.eql(u8, arg, "-fLLD")) {
+ use_lld = true;
+ } else if (mem.eql(u8, arg, "-fno-LLD")) {
+ use_lld = false;
+ } else if (mem.eql(u8, arg, "-fClang")) {
+ use_clang = true;
+ } else if (mem.eql(u8, arg, "-fno-Clang")) {
+ use_clang = false;
+ } else if (mem.eql(u8, arg, "-rdynamic")) {
+ rdynamic = true;
+ } else if (mem.eql(u8, arg, "-femit-bin")) {
+ emit_bin = .yes_default_path;
+ } else if (mem.startsWith(u8, arg, "-femit-bin=")) {
+ emit_bin = .{ .yes = arg["-femit-bin=".len..] };
+ } else if (mem.eql(u8, arg, "-fno-emit-bin")) {
+ emit_bin = .no;
+ } else if (mem.eql(u8, arg, "-femit-zir")) {
+ emit_zir = .yes_default_path;
+ } else if (mem.startsWith(u8, arg, "-femit-zir=")) {
+ emit_zir = .{ .yes = arg["-femit-zir=".len..] };
+ } else if (mem.eql(u8, arg, "-fno-emit-zir")) {
+ emit_zir = .no;
+ } else if (mem.eql(u8, arg, "-femit-h")) {
+ emit_h = .yes_default_path;
+ } else if (mem.startsWith(u8, arg, "-femit-h=")) {
+ emit_h = .{ .yes = arg["-femit-h=".len..] };
+ } else if (mem.eql(u8, arg, "-fno-emit-h")) {
+ emit_h = .no;
+ } else if (mem.eql(u8, arg, "-femit-asm")) {
+ emit_asm = .yes_default_path;
+ } else if (mem.startsWith(u8, arg, "-femit-asm=")) {
+ emit_asm = .{ .yes = arg["-femit-asm=".len..] };
+ } else if (mem.eql(u8, arg, "-fno-emit-asm")) {
+ emit_asm = .no;
+ } else if (mem.eql(u8, arg, "-femit-llvm-ir")) {
+ emit_llvm_ir = .yes_default_path;
+ } else if (mem.startsWith(u8, arg, "-femit-llvm-ir=")) {
+ emit_llvm_ir = .{ .yes = arg["-femit-llvm-ir=".len..] };
+ } else if (mem.eql(u8, arg, "-fno-emit-llvm-ir")) {
+ emit_llvm_ir = .no;
+ } else if (mem.eql(u8, arg, "-femit-docs")) {
+ emit_docs = .yes_default_path;
+ } else if (mem.startsWith(u8, arg, "-femit-docs=")) {
+ emit_docs = .{ .yes = arg["-femit-docs=".len..] };
+ } else if (mem.eql(u8, arg, "-fno-emit-docs")) {
+ emit_docs = .no;
+ } else if (mem.eql(u8, arg, "-femit-analysis")) {
+ emit_analysis = .yes_default_path;
+ } else if (mem.startsWith(u8, arg, "-femit-analysis=")) {
+ emit_analysis = .{ .yes = arg["-femit-analysis=".len..] };
+ } else if (mem.eql(u8, arg, "-fno-emit-analysis")) {
+ emit_analysis = .no;
+ } else if (mem.eql(u8, arg, "-dynamic")) {
+ link_mode = .Dynamic;
+ } else if (mem.eql(u8, arg, "-static")) {
+ link_mode = .Static;
+ } else if (mem.eql(u8, arg, "-fdll-export-fns")) {
+ dll_export_fns = true;
+ } else if (mem.eql(u8, arg, "-fno-dll-export-fns")) {
+ dll_export_fns = false;
+ } else if (mem.eql(u8, arg, "--show-builtin")) {
+ show_builtin = true;
+ emit_bin = .no;
+ } else if (mem.eql(u8, arg, "--strip")) {
+ strip = true;
+ } else if (mem.eql(u8, arg, "--single-threaded")) {
+ single_threaded = true;
+ } else if (mem.eql(u8, arg, "-ffunction-sections")) {
+ function_sections = true;
+ } else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
+ link_eh_frame_hdr = true;
+ } else if (mem.eql(u8, arg, "-Bsymbolic")) {
+ linker_bind_global_refs_locally = true;
+ } else if (mem.eql(u8, arg, "--verbose-link")) {
+ verbose_link = true;
+ } else if (mem.eql(u8, arg, "--verbose-cc")) {
+ verbose_cc = true;
+ } else if (mem.eql(u8, arg, "--verbose-tokenize")) {
+ verbose_tokenize = true;
+ } else if (mem.eql(u8, arg, "--verbose-ast")) {
+ verbose_ast = true;
+ } else if (mem.eql(u8, arg, "--verbose-ir")) {
+ verbose_ir = true;
+ } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
+ verbose_llvm_ir = true;
+ } else if (mem.eql(u8, arg, "--verbose-cimport")) {
+ verbose_cimport = true;
+ } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
+ verbose_llvm_cpu_features = true;
+ } else if (mem.startsWith(u8, arg, "-T")) {
+ linker_script = arg[2..];
+ } else if (mem.startsWith(u8, arg, "-L")) {
+ try lib_dirs.append(arg[2..]);
+ } else if (mem.startsWith(u8, arg, "-F")) {
+ try framework_dirs.append(arg[2..]);
+ } else if (mem.startsWith(u8, arg, "-l")) {
+ // We don't know whether this library is part of libc or libc++ until we resolve the target.
+ // So we simply append to the list for now.
+ try system_libs.append(arg[2..]);
+ } else if (mem.startsWith(u8, arg, "-D") or
+ mem.startsWith(u8, arg, "-I"))
+ {
+ try clang_argv.append(arg);
+ } else {
+ fatal("unrecognized parameter: '{}'", .{arg});
+ }
+ } else switch (Compilation.classifyFileExt(arg)) {
+ .object, .static_library => {
+ try link_objects.append(arg);
+ },
+ .assembly, .c, .cpp, .h, .ll, .bc => {
+ try c_source_files.append(.{
+ .src_path = arg,
+ .extra_flags = try arena.dupe([]const u8, extra_cflags.items),
+ });
+ },
+ .shared_library => {
+ fatal("linking against dynamic libraries not yet supported", .{});
+ },
+ .zig, .zir => {
+ if (root_src_file) |other| {
+ fatal("found another zig file '{}' after root source file '{}'", .{ arg, other });
+ } else {
+ root_src_file = arg;
+ }
+ },
+ .unknown => {
+ fatal("unrecognized file extension of parameter '{}'", .{arg});
+ },
+ }
+ }
+ if (optimize_mode_string) |s| {
+ optimize_mode = std.meta.stringToEnum(std.builtin.Mode, s) orelse
+ fatal("unrecognized optimization mode: '{}'", .{s});
+ }
+ },
+ .cc, .cpp => {
+ emit_h = .no;
+ strip = true;
+ ensure_libc_on_non_freestanding = true;
+ ensure_libcpp_on_non_freestanding = arg_mode == .cpp;
+ want_native_include_dirs = true;
+
+ const COutMode = enum {
+ link,
+ object,
+ assembly,
+ preprocessor,
+ };
+ var c_out_mode: COutMode = .link;
+ var out_path: ?[]const u8 = null;
+ var is_shared_lib = false;
+ var linker_args = std.ArrayList([]const u8).init(arena);
+ var it = ClangArgIterator.init(arena, all_args);
+ while (it.has_next) {
+ it.next() catch |err| {
+ fatal("unable to parse command line parameters: {}", .{@errorName(err)});
+ };
+ switch (it.zig_equivalent) {
+ .target => target_arch_os_abi = it.only_arg, // example: -target riscv64-linux-unknown
+ .o => out_path = it.only_arg, // -o
+ .c => c_out_mode = .object, // -c
+ .asm_only => c_out_mode = .assembly, // -S
+ .preprocess_only => c_out_mode = .preprocessor, // -E
+ .other => {
+ try clang_argv.appendSlice(it.other_args);
+ },
+ .positional => {
+ const file_ext = Compilation.classifyFileExt(mem.spanZ(it.only_arg));
+ switch (file_ext) {
+ .assembly, .c, .cpp, .ll, .bc, .h => try c_source_files.append(.{ .src_path = it.only_arg }),
+ .unknown, .shared_library, .object, .static_library => {
+ try link_objects.append(it.only_arg);
+ },
+ .zig, .zir => {
+ if (root_src_file) |other| {
+ fatal("found another zig file '{}' after root source file '{}'", .{ it.only_arg, other });
+ } else {
+ root_src_file = it.only_arg;
+ }
+ },
+ }
+ },
+ .l => {
+ // -l
+ // We don't know whether this library is part of libc or libc++ until we resolve the target.
+ // So we simply append to the list for now.
+ try system_libs.append(it.only_arg);
+ },
+ .ignore => {},
+ .driver_punt => {
+ // Never mind what we're doing, just pass the args directly. For example --help.
+ return punt_to_clang(arena, all_args);
+ },
+ .pic => want_pic = true,
+ .no_pic => want_pic = false,
+ .nostdlib => ensure_libc_on_non_freestanding = false,
+ .nostdlib_cpp => ensure_libcpp_on_non_freestanding = false,
+ .shared => {
+ link_mode = .Dynamic;
+ is_shared_lib = true;
+ },
+ .rdynamic => rdynamic = true,
+ .wl => {
+ var split_it = mem.split(it.only_arg, ",");
+ while (split_it.next()) |linker_arg| {
+ try linker_args.append(linker_arg);
+ }
+ },
+ .optimize => {
+ // Alright, what release mode do they want?
+ if (mem.eql(u8, it.only_arg, "Os")) {
+ optimize_mode = .ReleaseSmall;
+ } else if (mem.eql(u8, it.only_arg, "O2") or
+ mem.eql(u8, it.only_arg, "O3") or
+ mem.eql(u8, it.only_arg, "O4"))
+ {
+ optimize_mode = .ReleaseFast;
+ } else if (mem.eql(u8, it.only_arg, "Og") or
+ mem.eql(u8, it.only_arg, "O0"))
+ {
+ optimize_mode = .Debug;
+ } else {
+ try clang_argv.appendSlice(it.other_args);
+ }
+ },
+ .debug => {
+ strip = false;
+ if (mem.eql(u8, it.only_arg, "-g")) {
+ // We handled with strip = false above.
+ } else {
+ try clang_argv.appendSlice(it.other_args);
+ }
+ },
+ .sanitize => {
+ if (mem.eql(u8, it.only_arg, "undefined")) {
+ want_sanitize_c = true;
+ } else {
+ try clang_argv.appendSlice(it.other_args);
+ }
+ },
+ .linker_script => linker_script = it.only_arg,
+ .verbose_cmds => {
+ verbose_cc = true;
+ verbose_link = true;
+ },
+ .for_linker => try linker_args.append(it.only_arg),
+ .linker_input_z => {
+ try linker_args.append("-z");
+ try linker_args.append(it.only_arg);
+ },
+ .lib_dir => try lib_dirs.append(it.only_arg),
+ .mcpu => target_mcpu = it.only_arg,
+ .dep_file => {
+ disable_c_depfile = true;
+ try clang_argv.appendSlice(it.other_args);
+ },
+ .framework_dir => try framework_dirs.append(it.only_arg),
+ .framework => try frameworks.append(it.only_arg),
+ .nostdlibinc => want_native_include_dirs = false,
+ }
+ }
+ // Parse linker args.
+ var i: usize = 0;
+ while (i < linker_args.items.len) : (i += 1) {
+ const arg = linker_args.items[i];
+ if (mem.eql(u8, arg, "-soname")) {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
+ }
+ const soname = linker_args.items[i];
+ override_soname = soname;
+ // Use it as --name.
+ // Example: libsoundio.so.2
+ var prefix: usize = 0;
+ if (mem.startsWith(u8, soname, "lib")) {
+ prefix = 3;
+ }
+ var end: usize = soname.len;
+ if (mem.endsWith(u8, soname, ".so")) {
+ end -= 3;
+ } else {
+ var found_digit = false;
+ while (end > 0 and std.ascii.isDigit(soname[end - 1])) {
+ found_digit = true;
+ end -= 1;
+ }
+ if (found_digit and end > 0 and soname[end - 1] == '.') {
+ end -= 1;
+ } else {
+ end = soname.len;
+ }
+ if (mem.endsWith(u8, soname[prefix..end], ".so")) {
+ end -= 3;
+ }
+ }
+ provided_name = soname[prefix..end];
+ } else if (mem.eql(u8, arg, "-rpath")) {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
+ }
+ try rpath_list.append(linker_args.items[i]);
+ } else if (mem.eql(u8, arg, "-I") or
+ mem.eql(u8, arg, "--dynamic-linker") or
+ mem.eql(u8, arg, "-dynamic-linker"))
+ {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
+ }
+ target_dynamic_linker = linker_args.items[i];
+ } else if (mem.eql(u8, arg, "-E") or
+ mem.eql(u8, arg, "--export-dynamic") or
+ mem.eql(u8, arg, "-export-dynamic"))
+ {
+ rdynamic = true;
+ } else if (mem.eql(u8, arg, "--version-script")) {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
+ }
+ version_script = linker_args.items[i];
+ } else if (mem.startsWith(u8, arg, "-O")) {
+ try lld_argv.append(arg);
+ } else if (mem.eql(u8, arg, "--gc-sections")) {
+ linker_gc_sections = true;
+ } else if (mem.eql(u8, arg, "--no-gc-sections")) {
+ linker_gc_sections = false;
+ } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or
+ mem.eql(u8, arg, "-allow-shlib-undefined"))
+ {
+ linker_allow_shlib_undefined = true;
+ } else if (mem.eql(u8, arg, "--no-allow-shlib-undefined") or
+ mem.eql(u8, arg, "-no-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, "-z")) {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
+ }
+ const z_arg = linker_args.items[i];
+ if (mem.eql(u8, z_arg, "nodelete")) {
+ linker_z_nodelete = true;
+ } else if (mem.eql(u8, z_arg, "defs")) {
+ linker_z_defs = true;
+ } else {
+ warn("unsupported linker arg: -z {}", .{z_arg});
+ }
+ } else if (mem.eql(u8, arg, "--major-image-version")) {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
+ }
+ version.major = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
+ fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
+ };
+ have_version = true;
+ } else if (mem.eql(u8, arg, "--minor-image-version")) {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
+ }
+ version.minor = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
+ fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
+ };
+ have_version = true;
+ } else if (mem.eql(u8, arg, "--stack")) {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
+ }
+ stack_size_override = std.fmt.parseInt(u64, linker_args.items[i], 10) catch |err| {
+ fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
+ };
+ } else {
+ warn("unsupported linker arg: {}", .{arg});
+ }
+ }
+
+ if (want_sanitize_c) |wsc| {
+ if (wsc and optimize_mode == .ReleaseFast) {
+ optimize_mode = .ReleaseSafe;
+ }
+ }
+
+ switch (c_out_mode) {
+ .link => {
+ output_mode = if (is_shared_lib) .Lib else .Exe;
+ emit_bin = .{ .yes = out_path orelse "a.out" };
+ enable_cache = true;
+ },
+ .object => {
+ output_mode = .Obj;
+ if (out_path) |p| {
+ emit_bin = .{ .yes = p };
+ } else {
+ emit_bin = .yes_default_path;
+ }
+ },
+ .assembly => {
+ output_mode = .Obj;
+ emit_bin = .no;
+ if (out_path) |p| {
+ emit_asm = .{ .yes = p };
+ } else {
+ emit_asm = .yes_default_path;
+ }
+ },
+ .preprocessor => {
+ output_mode = .Obj;
+ // An error message is generated when there is more than 1 C source file.
+ if (c_source_files.items.len != 1) {
+ // For example `zig cc` and no args should print the "no input files" message.
+ return punt_to_clang(arena, all_args);
+ }
+ if (out_path) |p| {
+ emit_bin = .{ .yes = p };
+ clang_preprocessor_mode = .yes;
+ } else {
+ clang_preprocessor_mode = .stdout;
+ }
+ },
+ }
+ if (c_source_files.items.len == 0 and link_objects.items.len == 0) {
+ // For example `zig cc` and no args should print the "no input files" message.
+ return punt_to_clang(arena, all_args);
+ }
+ },
+ }
+
+ if (arg_mode == .translate_c and c_source_files.items.len != 1) {
+ fatal("translate-c expects exactly 1 source file (found {})", .{c_source_files.items.len});
+ }
+
+ const root_name = if (provided_name) |n| n else blk: {
+ if (arg_mode == .zig_test) {
+ break :blk "test";
+ } else if (root_src_file) |file| {
+ const basename = fs.path.basename(file);
+ break :blk mem.split(basename, ".").next().?;
+ } else if (c_source_files.items.len == 1) {
+ const basename = fs.path.basename(c_source_files.items[0].src_path);
+ break :blk mem.split(basename, ".").next().?;
+ } else if (link_objects.items.len == 1) {
+ const basename = fs.path.basename(link_objects.items[0]);
+ break :blk mem.split(basename, ".").next().?;
+ } else if (emit_bin == .yes) {
+ const basename = fs.path.basename(emit_bin.yes);
+ break :blk mem.split(basename, ".").next().?;
+ } else if (show_builtin) {
+ break :blk "builtin";
+ } else if (arg_mode == .run) {
+ break :blk "run";
+ } else {
+ fatal("--name [name] not provided and unable to infer", .{});
+ }
+ };
+
+ var diags: std.zig.CrossTarget.ParseOptions.Diagnostics = .{};
+ const cross_target = std.zig.CrossTarget.parse(.{
+ .arch_os_abi = target_arch_os_abi,
+ .cpu_features = target_mcpu,
+ .dynamic_linker = target_dynamic_linker,
+ .diagnostics = &diags,
+ }) catch |err| switch (err) {
+ error.UnknownCpuModel => {
+ help: {
+ var help_text = std.ArrayList(u8).init(arena);
+ for (diags.arch.?.allCpuModels()) |cpu| {
+ help_text.writer().print(" {}\n", .{cpu.name}) catch break :help;
+ }
+ std.log.info("Available CPUs for architecture '{}': {}", .{
+ @tagName(diags.arch.?), help_text.items,
+ });
+ }
+ fatal("Unknown CPU: '{}'", .{diags.cpu_name.?});
+ },
+ error.UnknownCpuFeature => {
+ help: {
+ var help_text = std.ArrayList(u8).init(arena);
+ for (diags.arch.?.allFeaturesList()) |feature| {
+ help_text.writer().print(" {}: {}\n", .{ feature.name, feature.description }) catch break :help;
+ }
+ std.log.info("Available CPU features for architecture '{}': {}", .{
+ @tagName(diags.arch.?), help_text.items,
+ });
+ }
+ fatal("Unknown CPU feature: '{}'", .{diags.unknown_feature_name});
+ },
+ else => |e| return e,
+ };
+
+ const target_info = try detectNativeTargetInfo(gpa, cross_target);
+
+ if (target_info.target.os.tag != .freestanding) {
+ if (ensure_libc_on_non_freestanding)
+ link_libc = true;
+ if (ensure_libcpp_on_non_freestanding)
+ link_libcpp = true;
+ }
+
+ // Now that we have target info, we can find out if any of the system libraries
+ // are part of libc or libc++. We remove them from the list and communicate their
+ // existence via flags instead.
+ {
+ var i: usize = 0;
+ while (i < system_libs.items.len) {
+ const lib_name = system_libs.items[i];
+ if (target_util.is_libc_lib_name(target_info.target, lib_name)) {
+ link_libc = true;
+ _ = system_libs.orderedRemove(i);
+ continue;
+ }
+ if (target_util.is_libcpp_lib_name(target_info.target, lib_name)) {
+ link_libcpp = true;
+ _ = system_libs.orderedRemove(i);
+ continue;
+ }
+ i += 1;
+ }
+ }
+
+ if (cross_target.isNativeOs() and (system_libs.items.len != 0 or want_native_include_dirs)) {
+ const paths = std.zig.system.NativePaths.detect(arena) catch |err| {
+ fatal("unable to detect native system paths: {}", .{@errorName(err)});
+ };
+ for (paths.warnings.items) |warning| {
+ warn("{}", .{warning});
+ }
+ try clang_argv.ensureCapacity(clang_argv.items.len + paths.include_dirs.items.len * 2);
+ for (paths.include_dirs.items) |include_dir| {
+ clang_argv.appendAssumeCapacity("-isystem");
+ clang_argv.appendAssumeCapacity(include_dir);
+ }
+ for (paths.lib_dirs.items) |lib_dir| {
+ try lib_dirs.append(lib_dir);
+ }
+ for (paths.rpaths.items) |rpath| {
+ try rpath_list.append(rpath);
+ }
+ }
+
+ const object_format: std.Target.ObjectFormat = blk: {
+ const ofmt = target_ofmt orelse break :blk target_info.target.getObjectFormat();
+ if (mem.eql(u8, ofmt, "elf")) {
+ break :blk .elf;
+ } else if (mem.eql(u8, ofmt, "c")) {
+ break :blk .c;
+ } else if (mem.eql(u8, ofmt, "coff")) {
+ break :blk .coff;
+ } else if (mem.eql(u8, ofmt, "pe")) {
+ break :blk .pe;
+ } else if (mem.eql(u8, ofmt, "macho")) {
+ break :blk .macho;
+ } else if (mem.eql(u8, ofmt, "wasm")) {
+ break :blk .wasm;
+ } else if (mem.eql(u8, ofmt, "hex")) {
+ break :blk .hex;
+ } else if (mem.eql(u8, ofmt, "raw")) {
+ break :blk .raw;
+ } else {
+ fatal("unsupported object format: {}", .{ofmt});
+ }
+ };
+
+ if (output_mode == .Obj and (object_format == .coff or object_format == .macho)) {
+ const total_obj_count = c_source_files.items.len +
+ @boolToInt(root_src_file != null) +
+ link_objects.items.len;
+ if (total_obj_count > 1) {
+ fatal("{s} does not support linking multiple objects into one", .{@tagName(object_format)});
+ }
+ }
+
+ var cleanup_emit_bin_dir: ?fs.Dir = null;
+ defer if (cleanup_emit_bin_dir) |*dir| dir.close();
+
+ const have_enable_cache = enable_cache orelse false;
+ const optional_version = if (have_version) version else null;
+
+ const emit_bin_loc: ?Compilation.EmitLoc = switch (emit_bin) {
+ .no => null,
+ .yes_default_path => Compilation.EmitLoc{
+ .directory = blk: {
+ switch (arg_mode) {
+ .run, .zig_test => break :blk null,
+ else => {
+ if (have_enable_cache) {
+ break :blk null;
+ } else {
+ break :blk .{ .path = null, .handle = fs.cwd() };
+ }
+ },
+ }
+ },
+ .basename = try std.zig.binNameAlloc(arena, .{
+ .root_name = root_name,
+ .target = target_info.target,
+ .output_mode = output_mode,
+ .link_mode = link_mode,
+ .object_format = object_format,
+ .version = optional_version,
+ }),
+ },
+ .yes => |full_path| b: {
+ const basename = fs.path.basename(full_path);
+ if (have_enable_cache) {
+ break :b Compilation.EmitLoc{
+ .basename = basename,
+ .directory = null,
+ };
+ }
+ if (fs.path.dirname(full_path)) |dirname| {
+ const handle = fs.cwd().openDir(dirname, .{}) catch |err| {
+ fatal("unable to open output directory '{}': {}", .{ dirname, @errorName(err) });
+ };
+ cleanup_emit_bin_dir = handle;
+ break :b Compilation.EmitLoc{
+ .basename = basename,
+ .directory = .{
+ .path = dirname,
+ .handle = handle,
+ },
+ };
+ } else {
+ break :b Compilation.EmitLoc{
+ .basename = basename,
+ .directory = .{ .path = null, .handle = fs.cwd() },
+ };
+ }
+ },
+ };
+
+ const default_h_basename = try std.fmt.allocPrint(arena, "{}.h", .{root_name});
+ var emit_h_resolved = try emit_h.resolve(default_h_basename);
+ defer emit_h_resolved.deinit();
+
+ const default_asm_basename = try std.fmt.allocPrint(arena, "{}.s", .{root_name});
+ var emit_asm_resolved = try emit_asm.resolve(default_asm_basename);
+ defer emit_asm_resolved.deinit();
+
+ const default_llvm_ir_basename = try std.fmt.allocPrint(arena, "{}.ll", .{root_name});
+ var emit_llvm_ir_resolved = try emit_llvm_ir.resolve(default_llvm_ir_basename);
+ defer emit_llvm_ir_resolved.deinit();
+
+ const default_analysis_basename = try std.fmt.allocPrint(arena, "{}-analysis.json", .{root_name});
+ var emit_analysis_resolved = try emit_analysis.resolve(default_analysis_basename);
+ defer emit_analysis_resolved.deinit();
+
+ var emit_docs_resolved = try emit_docs.resolve("docs");
+ defer emit_docs_resolved.deinit();
+
+ const zir_out_path: ?[]const u8 = switch (emit_zir) {
+ .no => null,
+ .yes_default_path => blk: {
+ if (root_src_file) |rsf| {
+ if (mem.endsWith(u8, rsf, ".zir")) {
+ break :blk try std.fmt.allocPrint(arena, "{}.out.zir", .{root_name});
+ }
+ }
+ break :blk try std.fmt.allocPrint(arena, "{}.zir", .{root_name});
+ },
+ .yes => |p| p,
+ };
+
+ var cleanup_root_dir: ?fs.Dir = null;
+ defer if (cleanup_root_dir) |*dir| dir.close();
+
+ const root_pkg: ?*Package = if (root_src_file) |src_path| blk: {
+ if (main_pkg_path) |p| {
+ const dir = try fs.cwd().openDir(p, .{});
+ cleanup_root_dir = dir;
+ root_pkg_memory.root_src_directory = .{ .path = p, .handle = dir };
+ root_pkg_memory.root_src_path = try fs.path.relative(arena, p, src_path);
+ } else {
+ root_pkg_memory.root_src_directory = .{ .path = null, .handle = fs.cwd() };
+ root_pkg_memory.root_src_path = src_path;
+ }
+ break :blk &root_pkg_memory;
+ } else null;
+
+ const self_exe_path = try fs.selfExePathAlloc(arena);
+ var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir|
+ .{
+ .path = lib_dir,
+ .handle = try fs.cwd().openDir(lib_dir, .{}),
+ }
+ else
+ introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
+ fatal("unable to find zig installation directory: {}", .{@errorName(err)});
+ };
+ defer zig_lib_directory.handle.close();
+
+ const random_seed = blk: {
+ var random_seed: u64 = undefined;
+ try std.crypto.randomBytes(mem.asBytes(&random_seed));
+ break :blk random_seed;
+ };
+ var default_prng = std.rand.DefaultPrng.init(random_seed);
+
+ var libc_installation: ?LibCInstallation = null;
+ defer if (libc_installation) |*l| l.deinit(gpa);
+
+ if (libc_paths_file) |paths_file| {
+ libc_installation = LibCInstallation.parse(gpa, paths_file) catch |err| {
+ fatal("unable to parse libc paths file: {}", .{@errorName(err)});
+ };
+ }
+
+ var global_cache_directory: Compilation.Directory = l: {
+ const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
+ break :l .{
+ .handle = try fs.cwd().makeOpenPath(p, .{}),
+ .path = p,
+ };
+ };
+ defer global_cache_directory.handle.close();
+
+ var cleanup_local_cache_dir: ?fs.Dir = null;
+ defer if (cleanup_local_cache_dir) |*dir| dir.close();
+
+ var local_cache_directory: Compilation.Directory = l: {
+ if (override_local_cache_dir) |local_cache_dir_path| {
+ const dir = try fs.cwd().makeOpenPath(local_cache_dir_path, .{});
+ cleanup_local_cache_dir = dir;
+ break :l .{
+ .handle = dir,
+ .path = local_cache_dir_path,
+ };
+ }
+ if (arg_mode == .run) {
+ break :l global_cache_directory;
+ }
+ const cache_dir_path = blk: {
+ if (root_pkg) |pkg| {
+ if (pkg.root_src_directory.path) |p| {
+ break :blk try fs.path.join(arena, &[_][]const u8{ p, "zig-cache" });
+ }
+ }
+ break :blk "zig-cache";
+ };
+ const cache_parent_dir = if (root_pkg) |pkg| pkg.root_src_directory.handle else fs.cwd();
+ const dir = try cache_parent_dir.makeOpenPath("zig-cache", .{});
+ cleanup_local_cache_dir = dir;
+ break :l .{
+ .handle = dir,
+ .path = cache_dir_path,
+ };
+ };
+
+ if (build_options.have_llvm and emit_asm != .no) {
+ // LLVM has no way to set this non-globally.
+ const argv = [_][*:0]const u8{ "zig (LLVM option parsing)", "--x86-asm-syntax=intel" };
+ @import("llvm.zig").ParseCommandLineOptions(argv.len, &argv);
+ }
+
+ gimmeMoreOfThoseSweetSweetFileDescriptors();
+
+ const comp = Compilation.create(gpa, .{
+ .zig_lib_directory = zig_lib_directory,
+ .local_cache_directory = local_cache_directory,
+ .global_cache_directory = global_cache_directory,
+ .root_name = root_name,
+ .target = target_info.target,
+ .is_native_os = cross_target.isNativeOs(),
+ .dynamic_linker = target_info.dynamic_linker.get(),
+ .output_mode = output_mode,
+ .root_pkg = root_pkg,
+ .emit_bin = emit_bin_loc,
+ .emit_h = emit_h_resolved.data,
+ .emit_asm = emit_asm_resolved.data,
+ .emit_llvm_ir = emit_llvm_ir_resolved.data,
+ .emit_docs = emit_docs_resolved.data,
+ .emit_analysis = emit_analysis_resolved.data,
+ .link_mode = link_mode,
+ .dll_export_fns = dll_export_fns,
+ .object_format = object_format,
+ .optimize_mode = optimize_mode,
+ .keep_source_files_loaded = zir_out_path != null,
+ .clang_argv = clang_argv.items,
+ .lld_argv = lld_argv.items,
+ .lib_dirs = lib_dirs.items,
+ .rpath_list = rpath_list.items,
+ .c_source_files = c_source_files.items,
+ .link_objects = link_objects.items,
+ .framework_dirs = framework_dirs.items,
+ .frameworks = frameworks.items,
+ .system_libs = system_libs.items,
+ .link_libc = link_libc,
+ .link_libcpp = link_libcpp,
+ .want_pic = want_pic,
+ .want_sanitize_c = want_sanitize_c,
+ .want_stack_check = want_stack_check,
+ .want_valgrind = want_valgrind,
+ .use_llvm = use_llvm,
+ .use_lld = use_lld,
+ .use_clang = use_clang,
+ .rdynamic = rdynamic,
+ .linker_script = linker_script,
+ .version_script = version_script,
+ .disable_c_depfile = disable_c_depfile,
+ .override_soname = override_soname,
+ .linker_gc_sections = linker_gc_sections,
+ .linker_allow_shlib_undefined = linker_allow_shlib_undefined,
+ .linker_bind_global_refs_locally = linker_bind_global_refs_locally,
+ .linker_z_nodelete = linker_z_nodelete,
+ .linker_z_defs = linker_z_defs,
+ .link_eh_frame_hdr = link_eh_frame_hdr,
+ .stack_size_override = stack_size_override,
+ .strip = strip,
+ .single_threaded = single_threaded,
+ .function_sections = function_sections,
+ .self_exe_path = self_exe_path,
+ .rand = &default_prng.random,
+ .clang_passthrough_mode = arg_mode != .build,
+ .clang_preprocessor_mode = clang_preprocessor_mode,
+ .version = optional_version,
+ .libc_installation = if (libc_installation) |*lci| lci else null,
+ .verbose_cc = verbose_cc,
+ .verbose_link = verbose_link,
+ .verbose_tokenize = verbose_tokenize,
+ .verbose_ast = verbose_ast,
+ .verbose_ir = verbose_ir,
+ .verbose_llvm_ir = verbose_llvm_ir,
+ .verbose_cimport = verbose_cimport,
+ .verbose_llvm_cpu_features = verbose_llvm_cpu_features,
+ .machine_code_model = machine_code_model,
+ .color = color,
+ .time_report = time_report,
+ .stack_report = stack_report,
+ .is_test = arg_mode == .zig_test,
+ .each_lib_rpath = each_lib_rpath,
+ .test_evented_io = test_evented_io,
+ .test_filter = test_filter,
+ .test_name_prefix = test_name_prefix,
+ .disable_lld_caching = !have_enable_cache,
+ .subsystem = subsystem,
+ }) catch |err| {
+ fatal("unable to create compilation: {}", .{@errorName(err)});
+ };
+ defer comp.destroy();
+
+ if (show_builtin) {
+ return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena));
+ }
+ if (arg_mode == .translate_c) {
+ return cmdTranslateC(comp, arena, have_enable_cache);
+ }
+
+ const hook: AfterUpdateHook = blk: {
+ if (!have_enable_cache)
+ break :blk .none;
+
+ switch (emit_bin) {
+ .no => break :blk .none,
+ .yes_default_path => break :blk .{
+ .print = comp.bin_file.options.emit.?.directory.path orelse ".",
+ },
+ .yes => |full_path| break :blk .{ .update = full_path },
+ }
+ };
+
+ try updateModule(gpa, comp, zir_out_path, hook);
+
+ if (build_options.is_stage1 and comp.stage1_lock != null and watch) {
+ warn("--watch is not recommended with the stage1 backend; it leaks memory and is not capable of incremental compilation", .{});
+ }
+
+ switch (arg_mode) {
+ .run, .zig_test => run: {
+ const exe_loc = emit_bin_loc orelse break :run;
+ const exe_directory = exe_loc.directory orelse comp.bin_file.options.emit.?.directory;
+ const exe_path = try fs.path.join(arena, &[_][]const u8{
+ exe_directory.path orelse ".", exe_loc.basename,
+ });
+
+ var argv = std.ArrayList([]const u8).init(gpa);
+ defer argv.deinit();
+
+ if (test_exec_args.items.len == 0) {
+ if (!std.Target.current.canExecBinariesOf(target_info.target)) {
+ switch (arg_mode) {
+ .zig_test => {
+ warn("created {s} but skipping execution because it is non-native", .{exe_path});
+ if (!watch) return cleanExit();
+ break :run;
+ },
+ .run => fatal("unable to execute {s}: non-native", .{exe_path}),
+ else => unreachable,
+ }
+ }
+ try argv.append(exe_path);
+ } else {
+ for (test_exec_args.items) |arg| {
+ try argv.append(arg orelse exe_path);
+ }
+ }
+ if (runtime_args_start) |i| {
+ try argv.appendSlice(all_args[i..]);
+ }
+ // TODO On operating systems that support it, do an execve here rather than child process,
+ // when watch=false and arg_mode == .run
+ const child = try std.ChildProcess.init(argv.items, gpa);
+ defer child.deinit();
+
+ child.stdin_behavior = .Inherit;
+ child.stdout_behavior = .Inherit;
+ child.stderr_behavior = .Inherit;
+
+ const term = try child.spawnAndWait();
+ switch (arg_mode) {
+ .run => {
+ switch (term) {
+ .Exited => |code| {
+ if (code == 0) {
+ if (!watch) return cleanExit();
+ } else {
+ // TODO https://github.com/ziglang/zig/issues/6342
+ process.exit(1);
+ }
+ },
+ else => process.exit(1),
+ }
+ },
+ .zig_test => {
+ switch (term) {
+ .Exited => |code| {
+ if (code == 0) {
+ if (!watch) return cleanExit();
+ } else {
+ const cmd = try argvCmd(arena, argv.items);
+ fatal("the following test command failed with exit code {}:\n{}", .{ code, cmd });
+ }
+ },
+ else => {
+ const cmd = try argvCmd(arena, argv.items);
+ fatal("the following test command crashed:\n{}", .{cmd});
+ },
+ }
+ },
+ else => unreachable,
+ }
+ },
+ else => {},
+ }
+
+ const stdin = std.io.getStdIn().inStream();
+ const stderr = std.io.getStdErr().outStream();
+ var repl_buf: [1024]u8 = undefined;
+
+ while (watch) {
+ try stderr.print("(zig) ", .{});
+ if (output_mode == .Exe) {
+ try comp.makeBinFileExecutable();
+ }
+ if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| {
+ try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)});
+ continue;
+ }) |line| {
+ const actual_line = mem.trimRight(u8, line, "\r\n ");
+
+ if (mem.eql(u8, actual_line, "update")) {
+ if (output_mode == .Exe) {
+ try comp.makeBinFileWritable();
+ }
+ try updateModule(gpa, comp, zir_out_path, hook);
+ } else if (mem.eql(u8, actual_line, "exit")) {
+ break;
+ } else if (mem.eql(u8, actual_line, "help")) {
+ try stderr.writeAll(repl_help);
+ } else {
+ try stderr.print("unknown command: {}\n", .{actual_line});
+ }
+ } else {
+ break;
+ }
+ }
+}
+
+const AfterUpdateHook = union(enum) {
+ none,
+ print: []const u8,
+ update: []const u8,
+};
+
+fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8, hook: AfterUpdateHook) !void {
+ try comp.update();
+
+ var errors = try comp.getAllErrorsAlloc();
+ defer errors.deinit(comp.gpa);
+
+ if (errors.list.len != 0) {
+ for (errors.list) |full_err_msg| {
+ full_err_msg.renderToStdErr();
+ }
+ } else switch (hook) {
+ .none => {},
+ .print => |bin_path| try io.getStdOut().writer().print("{s}\n", .{bin_path}),
+ .update => |full_path| _ = try comp.bin_file.options.emit.?.directory.handle.updateFile(
+ comp.bin_file.options.emit.?.sub_path,
+ fs.cwd(),
+ full_path,
+ .{},
+ ),
+ }
+
+ if (zir_out_path) |zop| {
+ const module = comp.bin_file.options.module orelse
+ fatal("-femit-zir with no zig source code", .{});
+ var new_zir_module = try zir.emit(gpa, module);
+ defer new_zir_module.deinit(gpa);
+
+ const baf = try io.BufferedAtomicFile.create(gpa, fs.cwd(), zop, .{});
+ defer baf.destroy();
+
+ try new_zir_module.writeToStream(gpa, baf.stream());
+
+ try baf.finish();
+ }
+}
+
+fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !void {
+ if (!build_options.have_llvm)
+ fatal("cannot translate-c: compiler built without LLVM extensions", .{});
+
+ assert(comp.c_source_files.len == 1);
+ const c_source_file = comp.c_source_files[0];
+
+ const translated_zig_basename = try std.fmt.allocPrint(arena, "{}.zig", .{comp.bin_file.options.root_name});
+
+ var man: Cache.Manifest = comp.obtainCObjectCacheManifest();
+ defer if (enable_cache) man.deinit();
+
+ man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects
+ _ = man.addFile(c_source_file.src_path, null) catch |err| {
+ fatal("unable to process '{}': {}", .{ c_source_file.src_path, @errorName(err) });
+ };
+
+ const digest = if (try man.hit()) man.final() else digest: {
+ var argv = std.ArrayList([]const u8).init(arena);
+
+ var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{});
+ defer zig_cache_tmp_dir.close();
+
+ const ext = Compilation.classifyFileExt(c_source_file.src_path);
+ const out_dep_path: ?[]const u8 = blk: {
+ if (comp.disable_c_depfile or !ext.clangSupportsDepFile())
+ break :blk null;
+
+ const c_src_basename = fs.path.basename(c_source_file.src_path);
+ const dep_basename = try std.fmt.allocPrint(arena, "{}.d", .{c_src_basename});
+ const out_dep_path = try comp.tmpFilePath(arena, dep_basename);
+ break :blk out_dep_path;
+ };
+
+ try comp.addTranslateCCArgs(arena, &argv, ext, out_dep_path);
+ try argv.append(c_source_file.src_path);
+
+ if (comp.verbose_cc) {
+ std.debug.print("clang ", .{});
+ Compilation.dump_argv(argv.items);
+ }
+
+ // Convert to null terminated args.
+ const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, argv.items.len + 1);
+ new_argv_with_sentinel[argv.items.len] = null;
+ const new_argv = new_argv_with_sentinel[0..argv.items.len :null];
+ for (argv.items) |arg, i| {
+ new_argv[i] = try arena.dupeZ(u8, arg);
+ }
+
+ const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"});
+ const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path);
+ var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{};
+ const tree = translate_c.translate(
+ comp.gpa,
+ new_argv.ptr,
+ new_argv.ptr + new_argv.len,
+ &clang_errors,
+ c_headers_dir_path_z,
+ ) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.ASTUnitFailure => fatal("clang API returned errors but due to a clang bug, it is not exposing the errors for zig to see. For more details: https://github.com/ziglang/zig/issues/4455", .{}),
+ error.SemanticAnalyzeFail => {
+ for (clang_errors) |clang_err| {
+ std.debug.print("{}:{}:{}: {}\n", .{
+ if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)",
+ clang_err.line + 1,
+ clang_err.column + 1,
+ clang_err.msg_ptr[0..clang_err.msg_len],
+ });
+ }
+ process.exit(1);
+ },
+ };
+ defer tree.deinit();
+
+ if (out_dep_path) |dep_file_path| {
+ const dep_basename = std.fs.path.basename(dep_file_path);
+ // Add the files depended on to the cache system.
+ try man.addDepFilePost(zig_cache_tmp_dir, dep_basename);
+ // Just to save disk space, we delete the file because it is never needed again.
+ zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| {
+ warn("failed to delete '{}': {}", .{ dep_file_path, @errorName(err) });
+ };
+ }
+
+ const digest = man.final();
+ const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest });
+ var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{});
+ defer o_dir.close();
+ var zig_file = try o_dir.createFile(translated_zig_basename, .{});
+ defer zig_file.close();
+
+ var bos = io.bufferedOutStream(zig_file.writer());
+ _ = try std.zig.render(comp.gpa, bos.writer(), tree);
+ try bos.flush();
+
+ man.writeManifest() catch |err| warn("failed to write cache manifest: {}", .{@errorName(err)});
+
+ break :digest digest;
+ };
+
+ if (enable_cache) {
+ const full_zig_path = try comp.local_cache_directory.join(arena, &[_][]const u8{
+ "o", &digest, translated_zig_basename,
+ });
+ try io.getStdOut().writer().print("{}\n", .{full_zig_path});
+ return cleanExit();
+ } else {
+ const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename });
+ const zig_file = try comp.local_cache_directory.handle.openFile(out_zig_path, .{});
+ defer zig_file.close();
+ try io.getStdOut().writeFileAll(zig_file, .{});
+ return cleanExit();
+ }
+}
+
+pub const usage_libc =
+ \\Usage: zig libc
+ \\
+ \\ Detect the native libc installation and print the resulting
+ \\ paths to stdout. You can save this into a file and then edit
+ \\ the paths to create a cross compilation libc kit. Then you
+ \\ can pass `--libc [file]` for Zig to use it.
+ \\
+ \\Usage: zig libc [paths_file]
+ \\
+ \\ Parse a libc installation text file and validate it.
+ \\
+;
+
+pub fn cmdLibC(gpa: *Allocator, args: []const []const u8) !void {
+ var input_file: ?[]const u8 = null;
+ {
+ var i: usize = 0;
+ while (i < args.len) : (i += 1) {
+ const arg = args[i];
+ if (mem.startsWith(u8, arg, "-")) {
+ if (mem.eql(u8, arg, "--help")) {
+ const stdout = io.getStdOut().writer();
+ try stdout.writeAll(usage_libc);
+ return cleanExit();
+ } else {
+ fatal("unrecognized parameter: '{}'", .{arg});
+ }
+ } else if (input_file != null) {
+ fatal("unexpected extra parameter: '{}'", .{arg});
+ } else {
+ input_file = arg;
+ }
+ }
+ }
+ if (input_file) |libc_file| {
+ var libc = LibCInstallation.parse(gpa, libc_file) catch |err| {
+ fatal("unable to parse libc file: {}", .{@errorName(err)});
+ };
+ defer libc.deinit(gpa);
+ } else {
+ var libc = LibCInstallation.findNative(.{
+ .allocator = gpa,
+ .verbose = true,
+ }) catch |err| {
+ fatal("unable to detect native libc: {}", .{@errorName(err)});
+ };
+ defer libc.deinit(gpa);
+
+ var bos = io.bufferedOutStream(io.getStdOut().writer());
+ try libc.render(bos.writer());
+ try bos.flush();
+ }
+}
+
+pub const usage_init =
+ \\Usage: zig init-exe
+ \\ zig init-lib
+ \\
+ \\ Initializes a `zig build` project in the current working
+ \\ directory.
+ \\
+ \\Options:
+ \\ --help Print this help and exit
+ \\
+ \\
+;
+
+pub fn cmdInit(
+ gpa: *Allocator,
+ arena: *Allocator,
+ args: []const []const u8,
+ output_mode: std.builtin.OutputMode,
+) !void {
+ {
+ var i: usize = 0;
+ while (i < args.len) : (i += 1) {
+ const arg = args[i];
+ if (mem.startsWith(u8, arg, "-")) {
+ if (mem.eql(u8, arg, "--help")) {
+ try io.getStdOut().writeAll(usage_init);
+ return cleanExit();
+ } else {
+ fatal("unrecognized parameter: '{}'", .{arg});
+ }
+ } else {
+ fatal("unexpected extra parameter: '{}'", .{arg});
+ }
+ }
+ }
+ const self_exe_path = try fs.selfExePathAlloc(arena);
+ var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
+ fatal("unable to find zig installation directory: {}\n", .{@errorName(err)});
+ };
+ defer zig_lib_directory.handle.close();
+
+ const s = fs.path.sep_str;
+ const template_sub_path = switch (output_mode) {
+ .Obj => unreachable,
+ .Lib => "std" ++ s ++ "special" ++ s ++ "init-lib",
+ .Exe => "std" ++ s ++ "special" ++ s ++ "init-exe",
+ };
+ var template_dir = try zig_lib_directory.handle.openDir(template_sub_path, .{});
+ defer template_dir.close();
+
+ const cwd_path = try process.getCwdAlloc(arena);
+ const cwd_basename = fs.path.basename(cwd_path);
+
+ const max_bytes = 10 * 1024 * 1024;
+ const build_zig_contents = template_dir.readFileAlloc(arena, "build.zig", max_bytes) catch |err| {
+ fatal("unable to read template file 'build.zig': {}", .{@errorName(err)});
+ };
+ var modified_build_zig_contents = std.ArrayList(u8).init(arena);
+ try modified_build_zig_contents.ensureCapacity(build_zig_contents.len);
+ for (build_zig_contents) |c| {
+ if (c == '$') {
+ try modified_build_zig_contents.appendSlice(cwd_basename);
+ } else {
+ try modified_build_zig_contents.append(c);
+ }
+ }
+ const main_zig_contents = template_dir.readFileAlloc(arena, "src" ++ s ++ "main.zig", max_bytes) catch |err| {
+ fatal("unable to read template file 'main.zig': {}", .{@errorName(err)});
+ };
+ if (fs.cwd().access("build.zig", .{})) |_| {
+ fatal("existing build.zig file would be overwritten", .{});
+ } else |err| switch (err) {
+ error.FileNotFound => {},
+ else => fatal("unable to test existence of build.zig: {}\n", .{@errorName(err)}),
+ }
+ var src_dir = try fs.cwd().makeOpenPath("src", .{});
+ defer src_dir.close();
+
+ try src_dir.writeFile("main.zig", main_zig_contents);
+ try fs.cwd().writeFile("build.zig", modified_build_zig_contents.items);
+
+ std.log.info("Created build.zig", .{});
+ std.log.info("Created src" ++ s ++ "main.zig", .{});
+
+ switch (output_mode) {
+ .Lib => std.log.info("Next, try `zig build --help` or `zig build test`", .{}),
+ .Exe => std.log.info("Next, try `zig build --help` or `zig build run`", .{}),
+ .Obj => unreachable,
+ }
+}
+
+pub const usage_build =
+ \\Usage: zig build [steps] [options]
+ \\
+ \\ Build a project from build.zig.
+ \\
+ \\Options:
+ \\ --help Print this help and exit
+ \\
+ \\
+;
+
+pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !void {
+ // We want to release all the locks before executing the child process, so we make a nice
+ // big block here to ensure the cleanup gets run when we extract out our argv.
+ const lock_and_argv = lock_and_argv: {
+ const self_exe_path = try fs.selfExePathAlloc(arena);
+
+ var build_file: ?[]const u8 = null;
+ var override_lib_dir: ?[]const u8 = null;
+ var override_global_cache_dir: ?[]const u8 = null;
+ var override_local_cache_dir: ?[]const u8 = null;
+ var child_argv = std.ArrayList([]const u8).init(arena);
+
+ const argv_index_exe = child_argv.items.len;
+ _ = try child_argv.addOne();
+
+ try child_argv.append(self_exe_path);
+
+ const argv_index_build_file = child_argv.items.len;
+ _ = try child_argv.addOne();
+
+ const argv_index_cache_dir = child_argv.items.len;
+ _ = try child_argv.addOne();
+
+ {
+ var i: usize = 0;
+ while (i < args.len) : (i += 1) {
+ const arg = args[i];
+ if (mem.startsWith(u8, arg, "-")) {
+ if (mem.eql(u8, arg, "--build-file")) {
+ if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg});
+ i += 1;
+ build_file = args[i];
+ continue;
+ } else if (mem.eql(u8, arg, "--override-lib-dir")) {
+ if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg});
+ i += 1;
+ override_lib_dir = args[i];
+ try child_argv.appendSlice(&[_][]const u8{ arg, args[i] });
+ continue;
+ } else if (mem.eql(u8, arg, "--cache-dir")) {
+ if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg});
+ i += 1;
+ override_local_cache_dir = args[i];
+ try child_argv.appendSlice(&[_][]const u8{ arg, args[i] });
+ continue;
+ } else if (mem.eql(u8, arg, "--global-cache-dir")) {
+ if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg});
+ i += 1;
+ override_global_cache_dir = args[i];
+ try child_argv.appendSlice(&[_][]const u8{ arg, args[i] });
+ continue;
+ }
+ }
+ try child_argv.append(arg);
+ }
+ }
+
+ var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir|
+ .{
+ .path = lib_dir,
+ .handle = try fs.cwd().openDir(lib_dir, .{}),
+ }
+ else
+ introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
+ fatal("unable to find zig installation directory: {}", .{@errorName(err)});
+ };
+ defer zig_lib_directory.handle.close();
+
+ const std_special = "std" ++ fs.path.sep_str ++ "special";
+ const special_dir_path = try zig_lib_directory.join(arena, &[_][]const u8{std_special});
+
+ var root_pkg: Package = .{
+ .root_src_directory = .{
+ .path = special_dir_path,
+ .handle = try zig_lib_directory.handle.openDir(std_special, .{}),
+ },
+ .root_src_path = "build_runner.zig",
+ };
+ defer root_pkg.root_src_directory.handle.close();
+
+ var cleanup_build_dir: ?fs.Dir = null;
+ defer if (cleanup_build_dir) |*dir| dir.close();
+
+ const cwd_path = try process.getCwdAlloc(arena);
+ const build_zig_basename = if (build_file) |bf| fs.path.basename(bf) else "build.zig";
+ const build_directory: Compilation.Directory = blk: {
+ if (build_file) |bf| {
+ if (fs.path.dirname(bf)) |dirname| {
+ const dir = try fs.cwd().openDir(dirname, .{});
+ cleanup_build_dir = dir;
+ break :blk .{ .path = dirname, .handle = dir };
+ }
+
+ break :blk .{ .path = null, .handle = fs.cwd() };
+ }
+ // Search up parent directories until we find build.zig.
+ var dirname: []const u8 = cwd_path;
+ while (true) {
+ const joined_path = try fs.path.join(arena, &[_][]const u8{ dirname, build_zig_basename });
+ if (fs.cwd().access(joined_path, .{})) |_| {
+ const dir = try fs.cwd().openDir(dirname, .{});
+ break :blk .{ .path = dirname, .handle = dir };
+ } else |err| switch (err) {
+ error.FileNotFound => {
+ dirname = fs.path.dirname(dirname) orelse {
+ std.log.info("{}", .{
+ \\Initialize a 'build.zig' template file with `zig init-lib` or `zig init-exe`,
+ \\or see `zig --help` for more options.
+ });
+ fatal("No 'build.zig' file found, in the current directory or any parent directories.", .{});
+ };
+ continue;
+ },
+ else => |e| return e,
+ }
+ }
+ };
+ child_argv.items[argv_index_build_file] = build_directory.path orelse cwd_path;
+
+ var build_pkg: Package = .{
+ .root_src_directory = build_directory,
+ .root_src_path = build_zig_basename,
+ };
+ try root_pkg.table.put(arena, "@build", &build_pkg);
+
+ var global_cache_directory: Compilation.Directory = l: {
+ const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
+ break :l .{
+ .handle = try fs.cwd().makeOpenPath(p, .{}),
+ .path = p,
+ };
+ };
+ defer global_cache_directory.handle.close();
+
+ var local_cache_directory: Compilation.Directory = l: {
+ if (override_local_cache_dir) |local_cache_dir_path| {
+ break :l .{
+ .handle = try fs.cwd().makeOpenPath(local_cache_dir_path, .{}),
+ .path = local_cache_dir_path,
+ };
+ }
+ const cache_dir_path = try build_directory.join(arena, &[_][]const u8{"zig-cache"});
+ break :l .{
+ .handle = try build_directory.handle.makeOpenPath("zig-cache", .{}),
+ .path = cache_dir_path,
+ };
+ };
+ defer local_cache_directory.handle.close();
+
+ child_argv.items[argv_index_cache_dir] = local_cache_directory.path orelse cwd_path;
+
+ gimmeMoreOfThoseSweetSweetFileDescriptors();
+
+ const cross_target: std.zig.CrossTarget = .{};
+ const target_info = try detectNativeTargetInfo(gpa, cross_target);
+
+ const exe_basename = try std.zig.binNameAlloc(arena, .{
+ .root_name = "build",
+ .target = target_info.target,
+ .output_mode = .Exe,
+ });
+ const emit_bin: Compilation.EmitLoc = .{
+ .directory = null, // Use the local zig-cache.
+ .basename = exe_basename,
+ };
+ const random_seed = blk: {
+ var random_seed: u64 = undefined;
+ try std.crypto.randomBytes(mem.asBytes(&random_seed));
+ break :blk random_seed;
+ };
+ var default_prng = std.rand.DefaultPrng.init(random_seed);
+ const comp = Compilation.create(gpa, .{
+ .zig_lib_directory = zig_lib_directory,
+ .local_cache_directory = local_cache_directory,
+ .global_cache_directory = global_cache_directory,
+ .root_name = "build",
+ .target = target_info.target,
+ .is_native_os = cross_target.isNativeOs(),
+ .dynamic_linker = target_info.dynamic_linker.get(),
+ .output_mode = .Exe,
+ .root_pkg = &root_pkg,
+ .emit_bin = emit_bin,
+ .emit_h = null,
+ .optimize_mode = .Debug,
+ .self_exe_path = self_exe_path,
+ .rand = &default_prng.random,
+ }) catch |err| {
+ fatal("unable to create compilation: {}", .{@errorName(err)});
+ };
+ defer comp.destroy();
+
+ try updateModule(gpa, comp, null, .none);
+
+ child_argv.items[argv_index_exe] = try comp.bin_file.options.emit.?.directory.join(
+ arena,
+ &[_][]const u8{exe_basename},
+ );
+
+ break :lock_and_argv .{
+ .child_argv = child_argv.items,
+ .lock = comp.bin_file.toOwnedLock(),
+ };
+ };
+ const child_argv = lock_and_argv.child_argv;
+ var lock = lock_and_argv.lock;
+ defer lock.release();
+
+ const child = try std.ChildProcess.init(child_argv, gpa);
+ defer child.deinit();
+
+ child.stdin_behavior = .Inherit;
+ child.stdout_behavior = .Inherit;
+ child.stderr_behavior = .Inherit;
+
+ const term = try child.spawnAndWait();
+ switch (term) {
+ .Exited => |code| {
+ if (code == 0) return cleanExit();
+ const cmd = try argvCmd(arena, child_argv);
+ fatal("the following build command failed with exit code {}:\n{}", .{ code, cmd });
+ },
+ else => {
+ const cmd = try argvCmd(arena, child_argv);
+ fatal("the following build command crashed:\n{}", .{cmd});
+ },
+ }
+}
+
+fn argvCmd(allocator: *Allocator, argv: []const []const u8) ![]u8 {
+ var cmd = std.ArrayList(u8).init(allocator);
+ defer cmd.deinit();
+ for (argv[0 .. argv.len - 1]) |arg| {
+ try cmd.appendSlice(arg);
+ try cmd.append(' ');
+ }
+ try cmd.appendSlice(argv[argv.len - 1]);
+ return cmd.toOwnedSlice();
+}
+
+pub const usage_fmt =
+ \\Usage: zig fmt [file]...
+ \\
+ \\ Formats the input files and modifies them in-place.
+ \\ Arguments can be files or directories, which are searched
+ \\ recursively.
+ \\
+ \\Options:
+ \\ --help Print this help and exit
+ \\ --color [auto|off|on] Enable or disable colored error messages
+ \\ --stdin Format code from stdin; output to stdout
+ \\ --check List non-conforming files and exit with an error
+ \\ if the list is non-empty
+ \\
+ \\
+;
+
+const Fmt = struct {
+ seen: SeenMap,
+ any_error: bool,
+ color: Color,
+ gpa: *Allocator,
+ out_buffer: std.ArrayList(u8),
+
+ const SeenMap = std.AutoHashMap(fs.File.INode, void);
+};
+
+pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
+ const stderr_file = io.getStdErr();
+ var color: Color = .Auto;
+ var stdin_flag: bool = false;
+ var check_flag: bool = false;
+ var input_files = ArrayList([]const u8).init(gpa);
+
+ {
+ var i: usize = 0;
+ while (i < args.len) : (i += 1) {
+ const arg = args[i];
+ if (mem.startsWith(u8, arg, "-")) {
+ if (mem.eql(u8, arg, "--help")) {
+ const stdout = io.getStdOut().outStream();
+ try stdout.writeAll(usage_fmt);
+ return cleanExit();
+ } else if (mem.eql(u8, arg, "--color")) {
+ if (i + 1 >= args.len) {
+ fatal("expected [auto|on|off] after --color", .{});
+ }
+ i += 1;
+ const next_arg = args[i];
+ if (mem.eql(u8, next_arg, "auto")) {
+ color = .Auto;
+ } else if (mem.eql(u8, next_arg, "on")) {
+ color = .On;
+ } else if (mem.eql(u8, next_arg, "off")) {
+ color = .Off;
+ } else {
+ fatal("expected [auto|on|off] after --color, found '{}'", .{next_arg});
+ }
+ } else if (mem.eql(u8, arg, "--stdin")) {
+ stdin_flag = true;
+ } else if (mem.eql(u8, arg, "--check")) {
+ check_flag = true;
+ } else {
+ fatal("unrecognized parameter: '{}'", .{arg});
+ }
+ } else {
+ try input_files.append(arg);
+ }
+ }
+ }
+
+ if (stdin_flag) {
+ if (input_files.items.len != 0) {
+ fatal("cannot use --stdin with positional arguments", .{});
+ }
+
+ const stdin = io.getStdIn().inStream();
+
+ const source_code = try stdin.readAllAlloc(gpa, max_src_size);
+ defer gpa.free(source_code);
+
+ const tree = std.zig.parse(gpa, source_code) catch |err| {
+ fatal("error parsing stdin: {}", .{err});
+ };
+ defer tree.deinit();
+
+ for (tree.errors) |parse_error| {
+ try printErrMsgToFile(gpa, parse_error, tree, "", stderr_file, color);
+ }
+ if (tree.errors.len != 0) {
+ process.exit(1);
+ }
+ if (check_flag) {
+ const anything_changed = try std.zig.render(gpa, io.null_out_stream, tree);
+ const code = if (anything_changed) @as(u8, 1) else @as(u8, 0);
+ process.exit(code);
+ }
+
+ var bos = io.bufferedOutStream(io.getStdOut().writer());
+ _ = try std.zig.render(gpa, bos.writer(), tree);
+ try bos.flush();
+ return;
+ }
+
+ if (input_files.items.len == 0) {
+ fatal("expected at least one source file argument", .{});
+ }
+
+ var fmt = Fmt{
+ .gpa = gpa,
+ .seen = Fmt.SeenMap.init(gpa),
+ .any_error = false,
+ .color = color,
+ .out_buffer = std.ArrayList(u8).init(gpa),
+ };
+ defer fmt.seen.deinit();
+ defer fmt.out_buffer.deinit();
+
+ for (input_files.span()) |file_path| {
+ // Get the real path here to avoid Windows failing on relative file paths with . or .. in them.
+ const real_path = fs.realpathAlloc(gpa, file_path) catch |err| {
+ fatal("unable to open '{}': {}", .{ file_path, err });
+ };
+ defer gpa.free(real_path);
+
+ try fmtPath(&fmt, file_path, check_flag, fs.cwd(), real_path);
+ }
+ if (fmt.any_error) {
+ process.exit(1);
+ }
+}
+
+const FmtError = error{
+ SystemResources,
+ OperationAborted,
+ IoPending,
+ BrokenPipe,
+ Unexpected,
+ WouldBlock,
+ FileClosed,
+ DestinationAddressRequired,
+ DiskQuota,
+ FileTooBig,
+ InputOutput,
+ NoSpaceLeft,
+ AccessDenied,
+ OutOfMemory,
+ RenameAcrossMountPoints,
+ ReadOnlyFileSystem,
+ LinkQuotaExceeded,
+ FileBusy,
+ EndOfStream,
+ Unseekable,
+ NotOpenForWriting,
+} || fs.File.OpenError;
+
+fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void {
+ fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) {
+ error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path),
+ else => {
+ warn("unable to format '{}': {}", .{ file_path, err });
+ fmt.any_error = true;
+ return;
+ },
+ };
+}
+
+fn fmtPathDir(
+ fmt: *Fmt,
+ file_path: []const u8,
+ check_mode: bool,
+ parent_dir: fs.Dir,
+ parent_sub_path: []const u8,
+) FmtError!void {
+ var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true });
+ defer dir.close();
+
+ const stat = try dir.stat();
+ if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
+
+ var dir_it = dir.iterate();
+ while (try dir_it.next()) |entry| {
+ const is_dir = entry.kind == .Directory;
+ if (is_dir or mem.endsWith(u8, entry.name, ".zig")) {
+ const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
+ defer fmt.gpa.free(full_path);
+
+ if (is_dir) {
+ try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);
+ } else {
+ fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| {
+ warn("unable to format '{}': {}", .{ full_path, err });
+ fmt.any_error = true;
+ return;
+ };
+ }
+ }
+ }
+}
+
+fn fmtPathFile(
+ fmt: *Fmt,
+ file_path: []const u8,
+ check_mode: bool,
+ dir: fs.Dir,
+ sub_path: []const u8,
+) FmtError!void {
+ const source_file = try dir.openFile(sub_path, .{});
+ var file_closed = false;
+ errdefer if (!file_closed) source_file.close();
+
+ const stat = try source_file.stat();
+
+ if (stat.kind == .Directory)
+ return error.IsDir;
+
+ const source_code = source_file.readToEndAllocOptions(
+ fmt.gpa,
+ max_src_size,
+ stat.size,
+ @alignOf(u8),
+ null,
+ ) catch |err| switch (err) {
+ error.ConnectionResetByPeer => unreachable,
+ error.ConnectionTimedOut => unreachable,
+ error.NotOpenForReading => unreachable,
+ else => |e| return e,
+ };
+ source_file.close();
+ file_closed = true;
+ defer fmt.gpa.free(source_code);
+
+ // Add to set after no longer possible to get error.IsDir.
+ if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
+
+ const tree = try std.zig.parse(fmt.gpa, source_code);
+ defer tree.deinit();
+
+ for (tree.errors) |parse_error| {
+ try printErrMsgToFile(fmt.gpa, parse_error, tree, file_path, std.io.getStdErr(), fmt.color);
+ }
+ if (tree.errors.len != 0) {
+ fmt.any_error = true;
+ return;
+ }
+
+ if (check_mode) {
+ const anything_changed = try std.zig.render(fmt.gpa, io.null_out_stream, tree);
+ if (anything_changed) {
+ // TODO this should output to stdout instead of stderr.
+ std.debug.print("{}\n", .{file_path});
+ fmt.any_error = true;
+ }
+ } else {
+ // As a heuristic, we make enough capacity for the same as the input source.
+ try fmt.out_buffer.ensureCapacity(source_code.len);
+ fmt.out_buffer.items.len = 0;
+ const writer = fmt.out_buffer.writer();
+ const anything_changed = try std.zig.render(fmt.gpa, writer, tree);
+ if (!anything_changed)
+ return; // Good thing we didn't waste any file system access on this.
+
+ var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode });
+ defer af.deinit();
+
+ try af.file.writeAll(fmt.out_buffer.items);
+ try af.finish();
+ // TODO this should output to stdout instead of stderr.
+ std.debug.print("{}\n", .{file_path});
+ }
+}
+
+fn printErrMsgToFile(
+ gpa: *mem.Allocator,
+ parse_error: ast.Error,
+ tree: *ast.Tree,
+ path: []const u8,
+ file: fs.File,
+ color: Color,
+) !void {
+ const color_on = switch (color) {
+ .Auto => file.isTty(),
+ .On => true,
+ .Off => false,
+ };
+ const lok_token = parse_error.loc();
+ const span_first = lok_token;
+ const span_last = lok_token;
+
+ const first_token = tree.token_locs[span_first];
+ const last_token = tree.token_locs[span_last];
+ const start_loc = tree.tokenLocationLoc(0, first_token);
+ const end_loc = tree.tokenLocationLoc(first_token.end, last_token);
+
+ var text_buf = std.ArrayList(u8).init(gpa);
+ defer text_buf.deinit();
+ const out_stream = text_buf.outStream();
+ try parse_error.render(tree.token_ids, out_stream);
+ const text = text_buf.span();
+
+ const stream = file.outStream();
+ try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text });
+
+ if (!color_on) return;
+
+ // Print \r and \t as one space each so that column counts line up
+ for (tree.source[start_loc.line_start..start_loc.line_end]) |byte| {
+ try stream.writeByte(switch (byte) {
+ '\r', '\t' => ' ',
+ else => byte,
+ });
+ }
+ try stream.writeByte('\n');
+ try stream.writeByteNTimes(' ', start_loc.column);
+ try stream.writeByteNTimes('~', last_token.end - first_token.start);
+ try stream.writeByte('\n');
+}
+
+pub const info_zen =
+ \\
+ \\ * Communicate intent precisely.
+ \\ * Edge cases matter.
+ \\ * Favor reading code over writing code.
+ \\ * Only one obvious way to do things.
+ \\ * Runtime crashes are better than bugs.
+ \\ * Compile errors are better than runtime crashes.
+ \\ * Incremental improvements.
+ \\ * Avoid local maximums.
+ \\ * Reduce the amount one must remember.
+ \\ * Focus on code rather than style.
+ \\ * Resource allocation may fail; resource deallocation must succeed.
+ \\ * Memory is a resource.
+ \\ * Together we serve the users.
+ \\
+ \\
+;
+
+extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
+
+/// TODO https://github.com/ziglang/zig/issues/3257
+fn punt_to_clang(arena: *Allocator, args: []const []const u8) error{OutOfMemory} {
+ if (!build_options.have_llvm)
+ fatal("`zig cc` and `zig c++` unavailable: compiler built without LLVM extensions", .{});
+ // Convert the args to the format Clang expects.
+ const argv = try arena.alloc(?[*:0]u8, args.len + 1);
+ for (args) |arg, i| {
+ argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation.
+ }
+ argv[args.len] = null;
+ const exit_code = ZigClang_main(@intCast(c_int, args.len), argv[0..args.len :null].ptr);
+ process.exit(@bitCast(u8, @truncate(i8, exit_code)));
+}
+
+const clang_args = @import("clang_options.zig").list;
+
+pub const ClangArgIterator = struct {
+ has_next: bool,
+ zig_equivalent: ZigEquivalent,
+ only_arg: []const u8,
+ second_arg: []const u8,
+ other_args: []const []const u8,
+ argv: []const []const u8,
+ next_index: usize,
+ root_args: ?*Args,
+ allocator: *Allocator,
+
+ pub const ZigEquivalent = enum {
+ target,
+ o,
+ c,
+ other,
+ positional,
+ l,
+ ignore,
+ driver_punt,
+ pic,
+ no_pic,
+ nostdlib,
+ nostdlib_cpp,
+ shared,
+ rdynamic,
+ wl,
+ preprocess_only,
+ asm_only,
+ optimize,
+ debug,
+ sanitize,
+ linker_script,
+ verbose_cmds,
+ for_linker,
+ linker_input_z,
+ lib_dir,
+ mcpu,
+ dep_file,
+ framework_dir,
+ framework,
+ nostdlibinc,
+ };
+
+ const Args = struct {
+ next_index: usize,
+ argv: []const []const u8,
+ };
+
+ fn init(allocator: *Allocator, argv: []const []const u8) ClangArgIterator {
+ return .{
+ .next_index = 2, // `zig cc foo` this points to `foo`
+ .has_next = argv.len > 2,
+ .zig_equivalent = undefined,
+ .only_arg = undefined,
+ .second_arg = undefined,
+ .other_args = undefined,
+ .argv = argv,
+ .root_args = null,
+ .allocator = allocator,
+ };
+ }
+
+ fn next(self: *ClangArgIterator) !void {
+ assert(self.has_next);
+ assert(self.next_index < self.argv.len);
+ // In this state we know that the parameter we are looking at is a root parameter
+ // rather than an argument to a parameter.
+ // We adjust the len below when necessary.
+ self.other_args = (self.argv.ptr + self.next_index)[0..1];
+ var arg = mem.span(self.argv[self.next_index]);
+ self.incrementArgIndex();
+
+ if (mem.startsWith(u8, arg, "@")) {
+ if (self.root_args != null) return error.NestedResponseFile;
+
+ // This is a "compiler response file". We must parse the file and treat its
+ // contents as command line parameters.
+ const allocator = self.allocator;
+ const max_bytes = 10 * 1024 * 1024; // 10 MiB of command line arguments is a reasonable limit
+ const resp_file_path = arg[1..];
+ const resp_contents = fs.cwd().readFileAlloc(allocator, resp_file_path, max_bytes) catch |err| {
+ fatal("unable to read response file '{}': {}", .{ resp_file_path, @errorName(err) });
+ };
+ defer allocator.free(resp_contents);
+ // TODO is there a specification for this file format? Let's find it and make this parsing more robust
+ // at the very least I'm guessing this needs to handle quotes and `#` comments.
+ var it = mem.tokenize(resp_contents, " \t\r\n");
+ var resp_arg_list = std.ArrayList([]const u8).init(allocator);
+ defer resp_arg_list.deinit();
+ {
+ errdefer {
+ for (resp_arg_list.span()) |item| {
+ allocator.free(mem.span(item));
+ }
+ }
+ while (it.next()) |token| {
+ const dupe_token = try mem.dupeZ(allocator, u8, token);
+ errdefer allocator.free(dupe_token);
+ try resp_arg_list.append(dupe_token);
+ }
+ const args = try allocator.create(Args);
+ errdefer allocator.destroy(args);
+ args.* = .{
+ .next_index = self.next_index,
+ .argv = self.argv,
+ };
+ self.root_args = args;
+ }
+ const resp_arg_slice = resp_arg_list.toOwnedSlice();
+ self.next_index = 0;
+ self.argv = resp_arg_slice;
+
+ if (resp_arg_slice.len == 0) {
+ self.resolveRespFileArgs();
+ return;
+ }
+
+ self.has_next = true;
+ self.other_args = (self.argv.ptr + self.next_index)[0..1]; // We adjust len below when necessary.
+ arg = mem.span(self.argv[self.next_index]);
+ self.incrementArgIndex();
+ }
+ if (!mem.startsWith(u8, arg, "-")) {
+ self.zig_equivalent = .positional;
+ self.only_arg = arg;
+ return;
+ }
+
+ find_clang_arg: for (clang_args) |clang_arg| switch (clang_arg.syntax) {
+ .flag => {
+ const prefix_len = clang_arg.matchEql(arg);
+ if (prefix_len > 0) {
+ self.zig_equivalent = clang_arg.zig_equivalent;
+ self.only_arg = arg[prefix_len..];
+
+ break :find_clang_arg;
+ }
+ },
+ .joined, .comma_joined => {
+ // joined example: --target=foo
+ // comma_joined example: -Wl,-soname,libsoundio.so.2
+ const prefix_len = clang_arg.matchStartsWith(arg);
+ if (prefix_len != 0) {
+ self.zig_equivalent = clang_arg.zig_equivalent;
+ self.only_arg = arg[prefix_len..]; // This will skip over the "--target=" part.
+
+ break :find_clang_arg;
+ }
+ },
+ .joined_or_separate => {
+ // Examples: `-lfoo`, `-l foo`
+ const prefix_len = clang_arg.matchStartsWith(arg);
+ if (prefix_len == arg.len) {
+ if (self.next_index >= self.argv.len) {
+ fatal("Expected parameter after '{}'", .{arg});
+ }
+ self.only_arg = self.argv[self.next_index];
+ self.incrementArgIndex();
+ self.other_args.len += 1;
+ self.zig_equivalent = clang_arg.zig_equivalent;
+
+ break :find_clang_arg;
+ } else if (prefix_len != 0) {
+ self.zig_equivalent = clang_arg.zig_equivalent;
+ self.only_arg = arg[prefix_len..];
+
+ break :find_clang_arg;
+ }
+ },
+ .joined_and_separate => {
+ // Example: `-Xopenmp-target=riscv64-linux-unknown foo`
+ const prefix_len = clang_arg.matchStartsWith(arg);
+ if (prefix_len != 0) {
+ self.only_arg = arg[prefix_len..];
+ if (self.next_index >= self.argv.len) {
+ fatal("Expected parameter after '{}'", .{arg});
+ }
+ self.second_arg = self.argv[self.next_index];
+ self.incrementArgIndex();
+ self.other_args.len += 1;
+ self.zig_equivalent = clang_arg.zig_equivalent;
+ break :find_clang_arg;
+ }
+ },
+ .separate => if (clang_arg.matchEql(arg) > 0) {
+ if (self.next_index >= self.argv.len) {
+ fatal("Expected parameter after '{}'", .{arg});
+ }
+ self.only_arg = self.argv[self.next_index];
+ self.incrementArgIndex();
+ self.other_args.len += 1;
+ self.zig_equivalent = clang_arg.zig_equivalent;
+ break :find_clang_arg;
+ },
+ .remaining_args_joined => {
+ const prefix_len = clang_arg.matchStartsWith(arg);
+ if (prefix_len != 0) {
+ @panic("TODO");
+ }
+ },
+ .multi_arg => if (clang_arg.matchEql(arg) > 0) {
+ @panic("TODO");
+ },
+ }
+ else {
+ fatal("Unknown Clang option: '{}'", .{arg});
+ }
+ }
+
+ fn incrementArgIndex(self: *ClangArgIterator) void {
+ self.next_index += 1;
+ self.resolveRespFileArgs();
+ }
+
+ fn resolveRespFileArgs(self: *ClangArgIterator) void {
+ const allocator = self.allocator;
+ if (self.next_index >= self.argv.len) {
+ if (self.root_args) |root_args| {
+ self.next_index = root_args.next_index;
+ self.argv = root_args.argv;
+
+ allocator.destroy(root_args);
+ self.root_args = null;
+ }
+ if (self.next_index >= self.argv.len) {
+ self.has_next = false;
+ }
+ }
+ }
+};
+
+fn parseCodeModel(arg: []const u8) std.builtin.CodeModel {
+ return std.meta.stringToEnum(std.builtin.CodeModel, arg) orelse
+ fatal("unsupported machine code model: '{}'", .{arg});
+}
+
+/// Raise the open file descriptor limit. Ask and ye shall receive.
+/// For one example of why this is handy, consider the case of building musl libc.
+/// We keep a lock open for each of the object files in the form of a file descriptor
+/// until they are finally put into an archive file. This is to allow a zig-cache
+/// garbage collector to run concurrently to zig processes, and to allow multiple
+/// zig processes to run concurrently with each other, without clobbering each other.
+fn gimmeMoreOfThoseSweetSweetFileDescriptors() void {
+ switch (std.Target.current.os.tag) {
+ .windows, .wasi, .uefi, .other, .freestanding => return,
+ // std lib is missing getrlimit/setrlimit.
+ // https://github.com/ziglang/zig/issues/6361
+ //else => {},
+ else => return,
+ }
+ const posix = std.os;
+ var lim = posix.getrlimit(posix.RLIMIT_NOFILE, &lim) catch return; // Oh well; we tried.
+ if (lim.cur == lim.max) return;
+ while (true) {
+ // Do a binary search for the limit.
+ var min: posix.rlim_t = lim.cur;
+ var max: posix.rlim_t = 1 << 20;
+ // But if there's a defined upper bound, don't search, just set it.
+ if (lim.max != posix.RLIM_INFINITY) {
+ min = lim.max;
+ max = lim.max;
+ }
+ while (true) {
+ lim.cur = min + (max - min) / 2;
+ if (posix.setrlimit(posix.RLIMIT_NOFILE, lim)) |_| {
+ min = lim.cur;
+ } else |_| {
+ max = lim.cur;
+ }
+ if (min + 1 < max) continue;
+ return;
+ }
+ }
+}
+
+test "fds" {
+ gimmeMoreOfThoseSweetSweetFileDescriptors();
+}
+
+fn detectNativeCpuWithLLVM(
+ arch: std.Target.Cpu.Arch,
+ llvm_cpu_name_z: ?[*:0]const u8,
+ llvm_cpu_features_opt: ?[*:0]const u8,
+) !std.Target.Cpu {
+ var result = std.Target.Cpu.baseline(arch);
+
+ if (llvm_cpu_name_z) |cpu_name_z| {
+ const llvm_cpu_name = mem.spanZ(cpu_name_z);
+
+ for (arch.allCpuModels()) |model| {
+ const this_llvm_name = model.llvm_name orelse continue;
+ if (mem.eql(u8, this_llvm_name, llvm_cpu_name)) {
+ // Here we use the non-dependencies-populated set,
+ // so that subtracting features later in this function
+ // affect the prepopulated set.
+ result = std.Target.Cpu{
+ .arch = arch,
+ .model = model,
+ .features = model.features,
+ };
+ break;
+ }
+ }
+ }
+
+ const all_features = arch.allFeaturesList();
+
+ if (llvm_cpu_features_opt) |llvm_cpu_features| {
+ var it = mem.tokenize(mem.spanZ(llvm_cpu_features), ",");
+ while (it.next()) |decorated_llvm_feat| {
+ var op: enum {
+ add,
+ sub,
+ } = undefined;
+ var llvm_feat: []const u8 = undefined;
+ if (mem.startsWith(u8, decorated_llvm_feat, "+")) {
+ op = .add;
+ llvm_feat = decorated_llvm_feat[1..];
+ } else if (mem.startsWith(u8, decorated_llvm_feat, "-")) {
+ op = .sub;
+ llvm_feat = decorated_llvm_feat[1..];
+ } else {
+ return error.InvalidLlvmCpuFeaturesFormat;
+ }
+ for (all_features) |feature, index_usize| {
+ const this_llvm_name = feature.llvm_name orelse continue;
+ if (mem.eql(u8, llvm_feat, this_llvm_name)) {
+ const index = @intCast(std.Target.Cpu.Feature.Set.Index, index_usize);
+ switch (op) {
+ .add => result.features.addFeature(index),
+ .sub => result.features.removeFeature(index),
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ result.features.populateDependencies(all_features);
+ return result;
+}
+
+fn detectNativeTargetInfo(gpa: *Allocator, cross_target: std.zig.CrossTarget) !std.zig.system.NativeTargetInfo {
+ var info = try std.zig.system.NativeTargetInfo.detect(gpa, cross_target);
+ if (info.cpu_detection_unimplemented) {
+ const arch = std.Target.current.cpu.arch;
+
+ // We want to just use detected_info.target but implementing
+ // CPU model & feature detection is todo so here we rely on LLVM.
+ // https://github.com/ziglang/zig/issues/4591
+ if (!build_options.have_llvm)
+ fatal("CPU features detection is not yet available for {} without LLVM extensions", .{@tagName(arch)});
+
+ const llvm = @import("llvm.zig");
+ const llvm_cpu_name = llvm.GetHostCPUName();
+ const llvm_cpu_features = llvm.GetNativeFeatures();
+ info.target.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features);
+ cross_target.updateCpuFeatures(&info.target.cpu.features);
+ info.target.cpu.arch = cross_target.getCpuArch();
+ }
+ return info;
+}
+
+/// Indicate that we are now terminating with a successful exit code.
+/// In debug builds, this is a no-op, so that the calling code's
+/// cleanup mechanisms are tested and so that external tools that
+/// check for resource leaks can be accurate. In release builds, this
+/// calls exit(0), and does not return.
+pub fn cleanExit() void {
+ if (std.builtin.mode == .Debug) {
+ return;
+ } else {
+ process.exit(0);
+ }
+}
diff --git a/src/mem_profile.cpp b/src/mem_profile.cpp
deleted file mode 100644
index 13ba57f913..0000000000
--- a/src/mem_profile.cpp
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (c) 2020 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#include "config.h"
-
-#ifdef ZIG_ENABLE_MEM_PROFILE
-
-#include "mem.hpp"
-#include "mem_list.hpp"
-#include "mem_profile.hpp"
-#include "heap.hpp"
-
-namespace mem {
-
-void Profile::init(const char *name, const char *kind) {
- this->name = name;
- this->kind = kind;
- this->usage_table.init(heap::bootstrap_allocator, 1024);
-}
-
-void Profile::deinit() {
- assert(this->name != nullptr);
- if (mem::report_print)
- this->print_report();
- this->usage_table.deinit(heap::bootstrap_allocator);
- this->name = nullptr;
-}
-
-void Profile::record_alloc(const TypeInfo &info, size_t count) {
- if (count == 0) return;
- auto existing_entry = this->usage_table.put_unique(
- heap::bootstrap_allocator,
- UsageKey{info.name_ptr, info.name_len},
- Entry{info, 1, count, 0, 0} );
- if (existing_entry != nullptr) {
- assert(existing_entry->value.info.size == info.size); // allocated name does not match type
- existing_entry->value.alloc.calls += 1;
- existing_entry->value.alloc.objects += count;
- }
-}
-
-void Profile::record_dealloc(const TypeInfo &info, size_t count) {
- if (count == 0) return;
- auto existing_entry = this->usage_table.maybe_get(UsageKey{info.name_ptr, info.name_len});
- if (existing_entry == nullptr) {
- fprintf(stderr, "deallocated name '");
- for (size_t i = 0; i < info.name_len; ++i)
- fputc(info.name_ptr[i], stderr);
- zig_panic("' (size %zu) not found in allocated table; compromised memory usage stats", info.size);
- }
- if (existing_entry->value.info.size != info.size) {
- fprintf(stderr, "deallocated name '");
- for (size_t i = 0; i < info.name_len; ++i)
- fputc(info.name_ptr[i], stderr);
- zig_panic("' does not match expected type size %zu", info.size);
- }
- assert(existing_entry->value.alloc.calls - existing_entry->value.dealloc.calls > 0);
- assert(existing_entry->value.alloc.objects - existing_entry->value.dealloc.objects >= count);
- existing_entry->value.dealloc.calls += 1;
- existing_entry->value.dealloc.objects += count;
-}
-
-static size_t entry_remain_total_bytes(const Profile::Entry *entry) {
- return (entry->alloc.objects - entry->dealloc.objects) * entry->info.size;
-}
-
-static int entry_compare(const void *a, const void *b) {
- size_t total_a = entry_remain_total_bytes(*reinterpret_cast(a));
- size_t total_b = entry_remain_total_bytes(*reinterpret_cast(b));
- if (total_a > total_b)
- return -1;
- if (total_a < total_b)
- return 1;
- return 0;
-};
-
-void Profile::print_report(FILE *file) {
- if (!file) {
- file = report_file;
- if (!file)
- file = stderr;
- }
- fprintf(file, "\n--- MEMORY PROFILE REPORT [%s]: %s ---\n", this->kind, this->name);
-
- List list;
- auto it = this->usage_table.entry_iterator();
- for (;;) {
- auto entry = it.next();
- if (!entry)
- break;
- list.append(&heap::bootstrap_allocator, &entry->value);
- }
-
- qsort(list.items, list.length, sizeof(const Entry *), entry_compare);
-
- size_t total_bytes_alloc = 0;
- size_t total_bytes_dealloc = 0;
-
- size_t total_calls_alloc = 0;
- size_t total_calls_dealloc = 0;
-
- for (size_t i = 0; i < list.length; i += 1) {
- const Entry *entry = list.at(i);
- fprintf(file, " ");
- for (size_t j = 0; j < entry->info.name_len; ++j)
- fputc(entry->info.name_ptr[j], file);
- fprintf(file, ": %zu bytes each", entry->info.size);
-
- fprintf(file, ", alloc{ %zu calls, %zu objects, total ", entry->alloc.calls, entry->alloc.objects);
- const auto alloc_num_bytes = entry->alloc.objects * entry->info.size;
- zig_pretty_print_bytes(file, alloc_num_bytes);
-
- fprintf(file, " }, dealloc{ %zu calls, %zu objects, total ", entry->dealloc.calls, entry->dealloc.objects);
- const auto dealloc_num_bytes = entry->dealloc.objects * entry->info.size;
- zig_pretty_print_bytes(file, dealloc_num_bytes);
-
- fprintf(file, " }, remain{ %zu calls, %zu objects, total ",
- entry->alloc.calls - entry->dealloc.calls,
- entry->alloc.objects - entry->dealloc.objects );
- const auto remain_num_bytes = alloc_num_bytes - dealloc_num_bytes;
- zig_pretty_print_bytes(file, remain_num_bytes);
-
- fprintf(file, " }\n");
-
- total_bytes_alloc += alloc_num_bytes;
- total_bytes_dealloc += dealloc_num_bytes;
-
- total_calls_alloc += entry->alloc.calls;
- total_calls_dealloc += entry->dealloc.calls;
- }
-
- fprintf(file, "\n Total bytes allocated: ");
- zig_pretty_print_bytes(file, total_bytes_alloc);
- fprintf(file, ", deallocated: ");
- zig_pretty_print_bytes(file, total_bytes_dealloc);
- fprintf(file, ", remaining: ");
- zig_pretty_print_bytes(file, total_bytes_alloc - total_bytes_dealloc);
-
- fprintf(file, "\n Total calls alloc: %zu, dealloc: %zu, remain: %zu\n",
- total_calls_alloc, total_calls_dealloc, (total_calls_alloc - total_calls_dealloc));
-
- list.deinit(&heap::bootstrap_allocator);
-}
-
-uint32_t Profile::usage_hash(UsageKey key) {
- // FNV 32-bit hash
- uint32_t h = 2166136261;
- for (size_t i = 0; i < key.name_len; ++i) {
- h = h ^ key.name_ptr[i];
- h = h * 16777619;
- }
- return h;
-}
-
-bool Profile::usage_equal(UsageKey a, UsageKey b) {
- return memcmp(a.name_ptr, b.name_ptr, a.name_len > b.name_len ? a.name_len : b.name_len) == 0;
-}
-
-void InternCounters::print_report(FILE *file) {
- if (!file) {
- file = report_file;
- if (!file)
- file = stderr;
- }
- fprintf(file, "\n--- IR INTERNING REPORT ---\n");
- fprintf(file, " undefined: interned %zu times\n", intern_counters.x_undefined);
- fprintf(file, " void: interned %zu times\n", intern_counters.x_void);
- fprintf(file, " null: interned %zu times\n", intern_counters.x_null);
- fprintf(file, " unreachable: interned %zu times\n", intern_counters.x_unreachable);
- fprintf(file, " zero_byte: interned %zu times\n", intern_counters.zero_byte);
-}
-
-InternCounters intern_counters;
-
-} // namespace mem
-
-#endif
diff --git a/src/mem_profile.hpp b/src/mem_profile.hpp
deleted file mode 100644
index 3b13b7680b..0000000000
--- a/src/mem_profile.hpp
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (c) 2020 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#ifndef ZIG_MEM_PROFILE_HPP
-#define ZIG_MEM_PROFILE_HPP
-
-#include "config.h"
-
-#ifdef ZIG_ENABLE_MEM_PROFILE
-
-#include
-
-#include "mem.hpp"
-#include "mem_hash_map.hpp"
-#include "util.hpp"
-
-namespace mem {
-
-struct Profile {
- void init(const char *name, const char *kind);
- void deinit();
-
- void record_alloc(const TypeInfo &info, size_t count);
- void record_dealloc(const TypeInfo &info, size_t count);
-
- void print_report(FILE *file = nullptr);
-
- struct Entry {
- TypeInfo info;
-
- struct Use {
- size_t calls;
- size_t objects;
- } alloc, dealloc;
- };
-
-private:
- const char *name;
- const char *kind;
-
- struct UsageKey {
- const char *name_ptr;
- size_t name_len;
- };
-
- static uint32_t usage_hash(UsageKey key);
- static bool usage_equal(UsageKey a, UsageKey b);
-
- HashMap usage_table;
-};
-
-struct InternCounters {
- size_t x_undefined;
- size_t x_void;
- size_t x_null;
- size_t x_unreachable;
- size_t zero_byte;
-
- void print_report(FILE *file = nullptr);
-};
-
-extern InternCounters intern_counters;
-
-} // namespace mem
-
-#endif
-#endif
diff --git a/src/mem_type_info.hpp b/src/mem_type_info.hpp
deleted file mode 100644
index 8698992ca0..0000000000
--- a/src/mem_type_info.hpp
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (c) 2020 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#ifndef ZIG_MEM_TYPE_INFO_HPP
-#define ZIG_MEM_TYPE_INFO_HPP
-
-#include "config.h"
-
-#ifndef ZIG_TYPE_INFO_IMPLEMENTATION
-# ifdef ZIG_ENABLE_MEM_PROFILE
-# define ZIG_TYPE_INFO_IMPLEMENTATION 1
-# else
-# define ZIG_TYPE_INFO_IMPLEMENTATION 0
-# endif
-#endif
-
-namespace mem {
-
-#if ZIG_TYPE_INFO_IMPLEMENTATION == 0
-
-struct TypeInfo {
- size_t size;
- size_t alignment;
-
- template
- static constexpr TypeInfo make() {
- return {sizeof(T), alignof(T)};
- }
-};
-
-#elif ZIG_TYPE_INFO_IMPLEMENTATION == 1
-
-//
-// A non-portable way to get a human-readable type-name compatible with
-// non-RTTI C++ compiler mode; eg. `-fno-rtti`.
-//
-// Minimum requirements are c++11 and a compiler that has a constant for the
-// current function's decorated name whereby a template-type name can be
-// computed. eg. `__PRETTY_FUNCTION__` or `__FUNCSIG__`.
-//
-// given the following snippet:
-//
-// | #include
-// |
-// | struct Top {};
-// | namespace mynamespace {
-// | using custom = unsigned int;
-// | struct Foo {
-// | struct Bar {};
-// | };
-// | };
-// |
-// | template
-// | void foobar() {
-// | #ifdef _MSC_VER
-// | fprintf(stderr, "--> %s\n", __FUNCSIG__);
-// | #else
-// | fprintf(stderr, "--> %s\n", __PRETTY_FUNCTION__);
-// | #endif
-// | }
-// |
-// | int main() {
-// | foobar();
-// | foobar();
-// | foobar();
-// | foobar();
-// | foobar();
-// | }
-//
-// gcc 9.2.0 produces:
-// --> void foobar() [with T = Top]
-// --> void foobar() [with T = unsigned int]
-// --> void foobar() [with T = unsigned int]
-// --> void foobar() [with T = mynamespace::Foo*]
-// --> void foobar() [with T = mynamespace::Foo::Bar*]
-//
-// xcode 11.3.1/clang produces:
-// --> void foobar() [T = Top]
-// --> void foobar() [T = unsigned int]
-// --> void foobar() [T = unsigned int]
-// --> void foobar() [T = mynamespace::Foo *]
-// --> void foobar() [T = mynamespace::Foo::Bar *]
-//
-// VStudio 2019 16.5.0/msvc produces:
-// --> void __cdecl foobar(void)
-// --> void __cdecl foobar(void)
-// --> void __cdecl foobar(void)
-// --> void __cdecl foobar(void)
-// --> void __cdecl foobar(void)
-//
-struct TypeInfo {
- const char *name_ptr;
- size_t name_len;
- size_t size;
- size_t alignment;
-
- static constexpr TypeInfo to_type_info(const char *str, size_t start, size_t end, size_t size, size_t alignment) {
- return TypeInfo{str + start, end - start, size, alignment};
- }
-
- static constexpr size_t index_of(const char *str, char c) {
- return *str == c ? 0 : 1 + index_of(str + 1, c);
- }
-
- template
- static constexpr const char *decorated_name() {
-#ifdef _MSC_VER
- return __FUNCSIG__;
-#else
- return __PRETTY_FUNCTION__;
-#endif
- }
-
- static constexpr TypeInfo extract(const char *decorated, size_t size, size_t alignment) {
-#ifdef _MSC_VER
- return to_type_info(decorated, index_of(decorated, '<') + 1, index_of(decorated, '>'), size, alignment);
-#else
- return to_type_info(decorated, index_of(decorated, '=') + 2, index_of(decorated, ']'), size, alignment);
-#endif
- }
-
- template
- static constexpr TypeInfo make() {
- return TypeInfo::extract(TypeInfo::decorated_name(), sizeof(T), alignof(T));
- }
-};
-
-#endif // ZIG_TYPE_INFO_IMPLEMENTATION
-
-} // namespace mem
-
-#endif
diff --git a/src/mingw.zig b/src/mingw.zig
new file mode 100644
index 0000000000..b6c8591ea4
--- /dev/null
+++ b/src/mingw.zig
@@ -0,0 +1,1092 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const mem = std.mem;
+const path = std.fs.path;
+const assert = std.debug.assert;
+const log = std.log.scoped(.mingw);
+
+const target_util = @import("target.zig");
+const Compilation = @import("Compilation.zig");
+const build_options = @import("build_options");
+const Cache = @import("Cache.zig");
+
+pub const CRTFile = enum {
+ crt2_o,
+ dllcrt2_o,
+ mingw32_lib,
+ msvcrt_os_lib,
+ mingwex_lib,
+ uuid_lib,
+};
+
+pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
+ if (!build_options.have_llvm) {
+ return error.ZigCompilerNotBuiltWithLLVMExtensions;
+ }
+ var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ switch (crt_file) {
+ .crt2_o => {
+ var args = std.ArrayList([]const u8).init(arena);
+ try add_cc_args(comp, arena, &args);
+ try args.appendSlice(&[_][]const u8{
+ "-U__CRTDLL__",
+ "-D__MSVCRT__",
+ // Uncomment these 3 things for crtu
+ //"-DUNICODE",
+ //"-D_UNICODE",
+ //"-DWPRFLAG=1",
+ });
+ return comp.build_crt_file("crt2", .Obj, &[1]Compilation.CSourceFile{
+ .{
+ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
+ "libc", "mingw", "crt", "crtexe.c",
+ }),
+ .extra_flags = args.items,
+ },
+ });
+ },
+
+ .dllcrt2_o => {
+ var args = std.ArrayList([]const u8).init(arena);
+ try add_cc_args(comp, arena, &args);
+ try args.appendSlice(&[_][]const u8{
+ "-U__CRTDLL__",
+ "-D__MSVCRT__",
+ });
+ return comp.build_crt_file("dllcrt2", .Obj, &[1]Compilation.CSourceFile{
+ .{
+ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
+ "libc", "mingw", "crt", "crtdll.c",
+ }),
+ .extra_flags = args.items,
+ },
+ });
+ },
+
+ .mingw32_lib => {
+ var c_source_files: [mingw32_lib_deps.len]Compilation.CSourceFile = undefined;
+ for (mingw32_lib_deps) |dep, i| {
+ var args = std.ArrayList([]const u8).init(arena);
+ try args.appendSlice(&[_][]const u8{
+ "-DHAVE_CONFIG_H",
+ "-D_SYSCRT=1",
+ "-DCRTDLL=1",
+
+ "-isystem",
+ try comp.zig_lib_directory.join(arena, &[_][]const u8{
+ "libc", "include", "any-windows-any",
+ }),
+
+ "-isystem",
+ try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "include" }),
+
+ "-std=gnu99",
+ "-D_CRTBLD",
+ "-D_WIN32_WINNT=0x0f00",
+ "-D__MSVCRT_VERSION__=0x700",
+ "-g",
+ "-O2",
+ });
+ c_source_files[i] = .{
+ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
+ "libc", "mingw", "crt", dep,
+ }),
+ .extra_flags = args.items,
+ };
+ }
+ return comp.build_crt_file("mingw32", .Lib, &c_source_files);
+ },
+
+ .msvcrt_os_lib => {
+ const extra_flags = try arena.dupe([]const u8, &[_][]const u8{
+ "-DHAVE_CONFIG_H",
+ "-D__LIBMSVCRT__",
+
+ "-I",
+ try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "include" }),
+
+ "-std=gnu99",
+ "-D_CRTBLD",
+ "-D_WIN32_WINNT=0x0f00",
+ "-D__MSVCRT_VERSION__=0x700",
+
+ "-isystem",
+ try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "include", "any-windows-any" }),
+
+ "-g",
+ "-O2",
+ });
+ var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena);
+
+ for (msvcrt_common_src) |dep| {
+ (try c_source_files.addOne()).* = .{
+ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", dep }),
+ .extra_flags = extra_flags,
+ };
+ }
+ if (comp.getTarget().cpu.arch == .i386) {
+ for (msvcrt_i386_src) |dep| {
+ (try c_source_files.addOne()).* = .{
+ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
+ "libc", "mingw", dep,
+ }),
+ .extra_flags = extra_flags,
+ };
+ }
+ } else {
+ for (msvcrt_other_src) |dep| {
+ (try c_source_files.addOne()).* = .{
+ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
+ "libc", "mingw", dep,
+ }),
+ .extra_flags = extra_flags,
+ };
+ }
+ }
+ return comp.build_crt_file("msvcrt-os", .Lib, c_source_files.items);
+ },
+
+ .mingwex_lib => {
+ const extra_flags = try arena.dupe([]const u8, &[_][]const u8{
+ "-DHAVE_CONFIG_H",
+
+ "-I",
+ try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw" }),
+
+ "-I",
+ try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "include" }),
+
+ "-std=gnu99",
+ "-D_CRTBLD",
+ "-D_WIN32_WINNT=0x0f00",
+ "-D__MSVCRT_VERSION__=0x700",
+ "-g",
+ "-O2",
+
+ "-isystem",
+ try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "include", "any-windows-any" }),
+ });
+ var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena);
+
+ for (mingwex_generic_src) |dep| {
+ (try c_source_files.addOne()).* = .{
+ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
+ "libc", "mingw", dep,
+ }),
+ .extra_flags = extra_flags,
+ };
+ }
+ const target = comp.getTarget();
+ if (target.cpu.arch == .i386 or target.cpu.arch == .x86_64) {
+ for (mingwex_x86_src) |dep| {
+ (try c_source_files.addOne()).* = .{
+ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
+ "libc", "mingw", dep,
+ }),
+ .extra_flags = extra_flags,
+ };
+ }
+ } else if (target.cpu.arch.isARM()) {
+ if (target.cpu.arch.ptrBitWidth() == 32) {
+ for (mingwex_arm32_src) |dep| {
+ (try c_source_files.addOne()).* = .{
+ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
+ "libc", "mingw", dep,
+ }),
+ .extra_flags = extra_flags,
+ };
+ }
+ } else {
+ for (mingwex_arm64_src) |dep| {
+ (try c_source_files.addOne()).* = .{
+ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
+ "libc", "mingw", dep,
+ }),
+ .extra_flags = extra_flags,
+ };
+ }
+ }
+ } else {
+ unreachable;
+ }
+ return comp.build_crt_file("mingwex", .Lib, c_source_files.items);
+ },
+
+ .uuid_lib => {
+ const extra_flags = try arena.dupe([]const u8, &[_][]const u8{
+ "-DHAVE_CONFIG_H",
+
+ "-I",
+ try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw" }),
+
+ "-I",
+ try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "include" }),
+
+ "-std=gnu99",
+ "-D_CRTBLD",
+ "-D_WIN32_WINNT=0x0f00",
+ "-D__MSVCRT_VERSION__=0x700",
+ "-g",
+ "-O2",
+
+ "-isystem",
+ try comp.zig_lib_directory.join(arena, &[_][]const u8{
+ "libc", "include", "any-windows-any",
+ }),
+ });
+ var c_source_files: [uuid_src.len]Compilation.CSourceFile = undefined;
+ for (uuid_src) |dep, i| {
+ c_source_files[i] = .{
+ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
+ "libc", "mingw", "libsrc", dep,
+ }),
+ .extra_flags = extra_flags,
+ };
+ }
+ return comp.build_crt_file("uuid", .Lib, &c_source_files);
+ },
+ }
+}
+
+fn add_cc_args(
+ comp: *Compilation,
+ arena: *Allocator,
+ args: *std.ArrayList([]const u8),
+) error{OutOfMemory}!void {
+ try args.appendSlice(&[_][]const u8{
+ "-DHAVE_CONFIG_H",
+
+ "-I",
+ try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "include" }),
+
+ "-isystem",
+ try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "include", "any-windows-any" }),
+ });
+
+ const target = comp.getTarget();
+ if (target.cpu.arch.isARM() and target.cpu.arch.ptrBitWidth() == 32) {
+ try args.append("-mfpu=vfp");
+ }
+
+ try args.appendSlice(&[_][]const u8{
+ "-std=gnu11",
+ "-D_CRTBLD",
+ "-D_WIN32_WINNT=0x0f00",
+ "-D__MSVCRT_VERSION__=0x700",
+ });
+}
+
+pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
+ var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ const def_file_path = findDef(comp, arena, lib_name) catch |err| switch (err) {
+ error.FileNotFound => {
+ log.debug("no {s}.def file available to make a DLL import {s}.lib", .{ lib_name, lib_name });
+ // In this case we will end up putting foo.lib onto the linker line and letting the linker
+ // use its library paths to look for libraries and report any problems.
+ return;
+ },
+ else => |e| return e,
+ };
+
+ // We need to invoke `zig clang` to use the preprocessor.
+ if (!build_options.have_llvm) return error.ZigCompilerNotBuiltWithLLVMExtensions;
+ const self_exe_path = comp.self_exe_path orelse return error.PreprocessorDisabled;
+
+ const target = comp.getTarget();
+
+ var cache: Cache = .{
+ .gpa = comp.gpa,
+ .manifest_dir = comp.cache_parent.manifest_dir,
+ };
+ cache.hash.addBytes(build_options.version);
+ cache.hash.addOptionalBytes(comp.zig_lib_directory.path);
+ cache.hash.add(target.cpu.arch);
+
+ var man = cache.obtain();
+ defer man.deinit();
+
+ _ = try man.addFile(def_file_path, null);
+
+ const final_lib_basename = try std.fmt.allocPrint(comp.gpa, "{s}.lib", .{lib_name});
+ errdefer comp.gpa.free(final_lib_basename);
+
+ if (try man.hit()) {
+ const digest = man.final();
+
+ try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1);
+ comp.crt_files.putAssumeCapacityNoClobber(final_lib_basename, .{
+ .full_object_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{
+ "o", &digest, final_lib_basename,
+ }),
+ .lock = man.toOwnedLock(),
+ });
+ return;
+ }
+
+ const digest = man.final();
+ const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
+ var o_dir = try comp.global_cache_directory.handle.makeOpenPath(o_sub_path, .{});
+ defer o_dir.close();
+
+ const final_def_basename = try std.fmt.allocPrint(arena, "{s}.def", .{lib_name});
+ const def_final_path = try comp.global_cache_directory.join(arena, &[_][]const u8{
+ "o", &digest, final_def_basename,
+ });
+
+ const target_def_arg = switch (target.cpu.arch) {
+ .i386 => "-DDEF_I386",
+ .x86_64 => "-DDEF_X64",
+ .arm, .armeb, .thumb, .thumbeb, .aarch64_32 => "-DDEF_ARM32",
+ .aarch64, .aarch64_be => "-DDEF_ARM64",
+ else => unreachable,
+ };
+
+ const args = [_][]const u8{
+ self_exe_path,
+ "clang",
+ "-x",
+ "c",
+ def_file_path,
+ "-Wp,-w",
+ "-undef",
+ "-P",
+ "-I",
+ try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "def-include" }),
+ target_def_arg,
+ "-E",
+ "-o",
+ def_final_path,
+ };
+
+ if (comp.verbose_cc) {
+ Compilation.dump_argv(&args);
+ }
+
+ const child = try std.ChildProcess.init(&args, arena);
+ defer child.deinit();
+
+ child.stdin_behavior = .Ignore;
+ child.stdout_behavior = .Pipe;
+ child.stderr_behavior = .Pipe;
+
+ try child.spawn();
+
+ const stdout_reader = child.stdout.?.reader();
+ const stderr_reader = child.stderr.?.reader();
+
+ // TODO https://github.com/ziglang/zig/issues/6343
+ const stdout = try stdout_reader.readAllAlloc(arena, std.math.maxInt(u32));
+ const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
+
+ const term = child.wait() catch |err| {
+ // TODO surface a proper error here
+ log.err("unable to spawn {}: {}", .{ args[0], @errorName(err) });
+ return error.ClangPreprocessorFailed;
+ };
+
+ switch (term) {
+ .Exited => |code| {
+ if (code != 0) {
+ // TODO surface a proper error here
+ log.err("clang exited with code {d} and stderr: {s}", .{ code, stderr });
+ return error.ClangPreprocessorFailed;
+ }
+ },
+ else => {
+ // TODO surface a proper error here
+ log.err("clang terminated unexpectedly with stderr: {}", .{stderr});
+ return error.ClangPreprocessorFailed;
+ },
+ }
+
+ const lib_final_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{
+ "o", &digest, final_lib_basename,
+ });
+ errdefer comp.gpa.free(lib_final_path);
+
+ const llvm = @import("llvm.zig");
+ const arch_type = @import("target.zig").archToLLVM(target.cpu.arch);
+ const def_final_path_z = try arena.dupeZ(u8, def_final_path);
+ const lib_final_path_z = try arena.dupeZ(u8, lib_final_path);
+ if (llvm.WriteImportLibrary(def_final_path_z.ptr, arch_type, lib_final_path_z.ptr, true)) {
+ // TODO surface a proper error here
+ log.err("unable to turn {s}.def into {s}.lib", .{ lib_name, lib_name });
+ return error.WritingImportLibFailed;
+ }
+
+ man.writeManifest() catch |err| {
+ log.warn("failed to write cache manifest for DLL import {s}.lib: {s}", .{ lib_name, @errorName(err) });
+ };
+
+ try comp.crt_files.putNoClobber(comp.gpa, final_lib_basename, .{
+ .full_object_path = lib_final_path,
+ .lock = man.toOwnedLock(),
+ });
+}
+
+/// This function body is verbose but all it does is test 3 different paths and see if a .def file exists.
+fn findDef(comp: *Compilation, allocator: *Allocator, lib_name: []const u8) ![]u8 {
+ const target = comp.getTarget();
+
+ const lib_path = switch (target.cpu.arch) {
+ .i386 => "lib32",
+ .x86_64 => "lib64",
+ .arm, .armeb => switch (target.cpu.arch.ptrBitWidth()) {
+ 32 => "libarm32",
+ 64 => "libarm64",
+ else => unreachable,
+ },
+ else => unreachable,
+ };
+
+ var override_path = std.ArrayList(u8).init(allocator);
+ defer override_path.deinit();
+
+ const s = path.sep_str;
+
+ {
+ // Try the archtecture-specific path first.
+ const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "{s}" ++ s ++ "{s}.def";
+ if (comp.zig_lib_directory.path) |p| {
+ try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_path, lib_name });
+ } else {
+ try override_path.writer().print(fmt_path, .{ lib_path, lib_name });
+ }
+ if (std.fs.cwd().access(override_path.items, .{})) |_| {
+ return override_path.toOwnedSlice();
+ } else |err| switch (err) {
+ error.FileNotFound => {},
+ else => |e| return e,
+ }
+ }
+
+ {
+ // Try the generic version.
+ override_path.shrinkRetainingCapacity(0);
+ const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def";
+ if (comp.zig_lib_directory.path) |p| {
+ try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name });
+ } else {
+ try override_path.writer().print(fmt_path, .{lib_name});
+ }
+ if (std.fs.cwd().access(override_path.items, .{})) |_| {
+ return override_path.toOwnedSlice();
+ } else |err| switch (err) {
+ error.FileNotFound => {},
+ else => |e| return e,
+ }
+ }
+
+ {
+ // Try the generic version and preprocess it.
+ override_path.shrinkRetainingCapacity(0);
+ const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def.in";
+ if (comp.zig_lib_directory.path) |p| {
+ try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name });
+ } else {
+ try override_path.writer().print(fmt_path, .{lib_name});
+ }
+ if (std.fs.cwd().access(override_path.items, .{})) |_| {
+ return override_path.toOwnedSlice();
+ } else |err| switch (err) {
+ error.FileNotFound => {},
+ else => |e| return e,
+ }
+ }
+
+ return error.FileNotFound;
+}
+
+const mingw32_lib_deps = [_][]const u8{
+ "crt0_c.c",
+ "dll_argv.c",
+ "gccmain.c",
+ "natstart.c",
+ "pseudo-reloc-list.c",
+ "wildcard.c",
+ "charmax.c",
+ "crt0_w.c",
+ "dllargv.c",
+ "gs_support.c",
+ "_newmode.c",
+ "tlssup.c",
+ "xncommod.c",
+ "cinitexe.c",
+ "merr.c",
+ "usermatherr.c",
+ "pesect.c",
+ "udllargc.c",
+ "xthdloc.c",
+ "CRT_fp10.c",
+ "mingw_helpers.c",
+ "pseudo-reloc.c",
+ "udll_argv.c",
+ "xtxtmode.c",
+ "crt_handler.c",
+ "tlsthrd.c",
+ "tlsmthread.c",
+ "tlsmcrt.c",
+ "cxa_atexit.c",
+};
+const msvcrt_common_src = [_][]const u8{
+ "misc" ++ path.sep_str ++ "_create_locale.c",
+ "misc" ++ path.sep_str ++ "_free_locale.c",
+ "misc" ++ path.sep_str ++ "onexit_table.c",
+ "misc" ++ path.sep_str ++ "register_tls_atexit.c",
+ "stdio" ++ path.sep_str ++ "acrt_iob_func.c",
+ "misc" ++ path.sep_str ++ "_configthreadlocale.c",
+ "misc" ++ path.sep_str ++ "_get_current_locale.c",
+ "misc" ++ path.sep_str ++ "invalid_parameter_handler.c",
+ "misc" ++ path.sep_str ++ "output_format.c",
+ "misc" ++ path.sep_str ++ "purecall.c",
+ "secapi" ++ path.sep_str ++ "_access_s.c",
+ "secapi" ++ path.sep_str ++ "_cgets_s.c",
+ "secapi" ++ path.sep_str ++ "_cgetws_s.c",
+ "secapi" ++ path.sep_str ++ "_chsize_s.c",
+ "secapi" ++ path.sep_str ++ "_controlfp_s.c",
+ "secapi" ++ path.sep_str ++ "_cprintf_s.c",
+ "secapi" ++ path.sep_str ++ "_cprintf_s_l.c",
+ "secapi" ++ path.sep_str ++ "_ctime32_s.c",
+ "secapi" ++ path.sep_str ++ "_ctime64_s.c",
+ "secapi" ++ path.sep_str ++ "_cwprintf_s.c",
+ "secapi" ++ path.sep_str ++ "_cwprintf_s_l.c",
+ "secapi" ++ path.sep_str ++ "_gmtime32_s.c",
+ "secapi" ++ path.sep_str ++ "_gmtime64_s.c",
+ "secapi" ++ path.sep_str ++ "_localtime32_s.c",
+ "secapi" ++ path.sep_str ++ "_localtime64_s.c",
+ "secapi" ++ path.sep_str ++ "_mktemp_s.c",
+ "secapi" ++ path.sep_str ++ "_sopen_s.c",
+ "secapi" ++ path.sep_str ++ "_strdate_s.c",
+ "secapi" ++ path.sep_str ++ "_strtime_s.c",
+ "secapi" ++ path.sep_str ++ "_umask_s.c",
+ "secapi" ++ path.sep_str ++ "_vcprintf_s.c",
+ "secapi" ++ path.sep_str ++ "_vcprintf_s_l.c",
+ "secapi" ++ path.sep_str ++ "_vcwprintf_s.c",
+ "secapi" ++ path.sep_str ++ "_vcwprintf_s_l.c",
+ "secapi" ++ path.sep_str ++ "_vscprintf_p.c",
+ "secapi" ++ path.sep_str ++ "_vscwprintf_p.c",
+ "secapi" ++ path.sep_str ++ "_vswprintf_p.c",
+ "secapi" ++ path.sep_str ++ "_waccess_s.c",
+ "secapi" ++ path.sep_str ++ "_wasctime_s.c",
+ "secapi" ++ path.sep_str ++ "_wctime32_s.c",
+ "secapi" ++ path.sep_str ++ "_wctime64_s.c",
+ "secapi" ++ path.sep_str ++ "_wstrtime_s.c",
+ "secapi" ++ path.sep_str ++ "_wmktemp_s.c",
+ "secapi" ++ path.sep_str ++ "_wstrdate_s.c",
+ "secapi" ++ path.sep_str ++ "asctime_s.c",
+ "secapi" ++ path.sep_str ++ "memcpy_s.c",
+ "secapi" ++ path.sep_str ++ "memmove_s.c",
+ "secapi" ++ path.sep_str ++ "rand_s.c",
+ "secapi" ++ path.sep_str ++ "sprintf_s.c",
+ "secapi" ++ path.sep_str ++ "strerror_s.c",
+ "secapi" ++ path.sep_str ++ "vsprintf_s.c",
+ "secapi" ++ path.sep_str ++ "wmemcpy_s.c",
+ "secapi" ++ path.sep_str ++ "wmemmove_s.c",
+ "stdio" ++ path.sep_str ++ "mingw_lock.c",
+};
+const msvcrt_i386_src = [_][]const u8{
+ "misc" ++ path.sep_str ++ "lc_locale_func.c",
+ "misc" ++ path.sep_str ++ "___mb_cur_max_func.c",
+};
+
+const msvcrt_other_src = [_][]const u8{
+ "misc" ++ path.sep_str ++ "__p___argv.c",
+ "misc" ++ path.sep_str ++ "__p__acmdln.c",
+ "misc" ++ path.sep_str ++ "__p__fmode.c",
+ "misc" ++ path.sep_str ++ "__p__wcmdln.c",
+};
+const mingwex_generic_src = [_][]const u8{
+ "complex" ++ path.sep_str ++ "_cabs.c",
+ "complex" ++ path.sep_str ++ "cabs.c",
+ "complex" ++ path.sep_str ++ "cabsf.c",
+ "complex" ++ path.sep_str ++ "cabsl.c",
+ "complex" ++ path.sep_str ++ "cacos.c",
+ "complex" ++ path.sep_str ++ "cacosf.c",
+ "complex" ++ path.sep_str ++ "cacosl.c",
+ "complex" ++ path.sep_str ++ "carg.c",
+ "complex" ++ path.sep_str ++ "cargf.c",
+ "complex" ++ path.sep_str ++ "cargl.c",
+ "complex" ++ path.sep_str ++ "casin.c",
+ "complex" ++ path.sep_str ++ "casinf.c",
+ "complex" ++ path.sep_str ++ "casinl.c",
+ "complex" ++ path.sep_str ++ "catan.c",
+ "complex" ++ path.sep_str ++ "catanf.c",
+ "complex" ++ path.sep_str ++ "catanl.c",
+ "complex" ++ path.sep_str ++ "ccos.c",
+ "complex" ++ path.sep_str ++ "ccosf.c",
+ "complex" ++ path.sep_str ++ "ccosl.c",
+ "complex" ++ path.sep_str ++ "cexp.c",
+ "complex" ++ path.sep_str ++ "cexpf.c",
+ "complex" ++ path.sep_str ++ "cexpl.c",
+ "complex" ++ path.sep_str ++ "cimag.c",
+ "complex" ++ path.sep_str ++ "cimagf.c",
+ "complex" ++ path.sep_str ++ "cimagl.c",
+ "complex" ++ path.sep_str ++ "clog.c",
+ "complex" ++ path.sep_str ++ "clog10.c",
+ "complex" ++ path.sep_str ++ "clog10f.c",
+ "complex" ++ path.sep_str ++ "clog10l.c",
+ "complex" ++ path.sep_str ++ "clogf.c",
+ "complex" ++ path.sep_str ++ "clogl.c",
+ "complex" ++ path.sep_str ++ "conj.c",
+ "complex" ++ path.sep_str ++ "conjf.c",
+ "complex" ++ path.sep_str ++ "conjl.c",
+ "complex" ++ path.sep_str ++ "cpow.c",
+ "complex" ++ path.sep_str ++ "cpowf.c",
+ "complex" ++ path.sep_str ++ "cpowl.c",
+ "complex" ++ path.sep_str ++ "cproj.c",
+ "complex" ++ path.sep_str ++ "cprojf.c",
+ "complex" ++ path.sep_str ++ "cprojl.c",
+ "complex" ++ path.sep_str ++ "creal.c",
+ "complex" ++ path.sep_str ++ "crealf.c",
+ "complex" ++ path.sep_str ++ "creall.c",
+ "complex" ++ path.sep_str ++ "csin.c",
+ "complex" ++ path.sep_str ++ "csinf.c",
+ "complex" ++ path.sep_str ++ "csinl.c",
+ "complex" ++ path.sep_str ++ "csqrt.c",
+ "complex" ++ path.sep_str ++ "csqrtf.c",
+ "complex" ++ path.sep_str ++ "csqrtl.c",
+ "complex" ++ path.sep_str ++ "ctan.c",
+ "complex" ++ path.sep_str ++ "ctanf.c",
+ "complex" ++ path.sep_str ++ "ctanl.c",
+ "crt" ++ path.sep_str ++ "dllentry.c",
+ "crt" ++ path.sep_str ++ "dllmain.c",
+ "gdtoa" ++ path.sep_str ++ "arithchk.c",
+ "gdtoa" ++ path.sep_str ++ "dmisc.c",
+ "gdtoa" ++ path.sep_str ++ "dtoa.c",
+ "gdtoa" ++ path.sep_str ++ "g__fmt.c",
+ "gdtoa" ++ path.sep_str ++ "g_dfmt.c",
+ "gdtoa" ++ path.sep_str ++ "g_ffmt.c",
+ "gdtoa" ++ path.sep_str ++ "g_xfmt.c",
+ "gdtoa" ++ path.sep_str ++ "gdtoa.c",
+ "gdtoa" ++ path.sep_str ++ "gethex.c",
+ "gdtoa" ++ path.sep_str ++ "gmisc.c",
+ "gdtoa" ++ path.sep_str ++ "hd_init.c",
+ "gdtoa" ++ path.sep_str ++ "hexnan.c",
+ "gdtoa" ++ path.sep_str ++ "misc.c",
+ "gdtoa" ++ path.sep_str ++ "qnan.c",
+ "gdtoa" ++ path.sep_str ++ "smisc.c",
+ "gdtoa" ++ path.sep_str ++ "strtodg.c",
+ "gdtoa" ++ path.sep_str ++ "strtodnrp.c",
+ "gdtoa" ++ path.sep_str ++ "strtof.c",
+ "gdtoa" ++ path.sep_str ++ "strtopx.c",
+ "gdtoa" ++ path.sep_str ++ "sum.c",
+ "gdtoa" ++ path.sep_str ++ "ulp.c",
+ "math" ++ path.sep_str ++ "abs64.c",
+ "math" ++ path.sep_str ++ "cbrt.c",
+ "math" ++ path.sep_str ++ "cbrtf.c",
+ "math" ++ path.sep_str ++ "cbrtl.c",
+ "math" ++ path.sep_str ++ "cephes_emath.c",
+ "math" ++ path.sep_str ++ "copysign.c",
+ "math" ++ path.sep_str ++ "copysignf.c",
+ "math" ++ path.sep_str ++ "coshf.c",
+ "math" ++ path.sep_str ++ "coshl.c",
+ "math" ++ path.sep_str ++ "erfl.c",
+ "math" ++ path.sep_str ++ "expf.c",
+ "math" ++ path.sep_str ++ "fabs.c",
+ "math" ++ path.sep_str ++ "fabsf.c",
+ "math" ++ path.sep_str ++ "fabsl.c",
+ "math" ++ path.sep_str ++ "fdim.c",
+ "math" ++ path.sep_str ++ "fdimf.c",
+ "math" ++ path.sep_str ++ "fdiml.c",
+ "math" ++ path.sep_str ++ "fma.c",
+ "math" ++ path.sep_str ++ "fmaf.c",
+ "math" ++ path.sep_str ++ "fmal.c",
+ "math" ++ path.sep_str ++ "fmax.c",
+ "math" ++ path.sep_str ++ "fmaxf.c",
+ "math" ++ path.sep_str ++ "fmaxl.c",
+ "math" ++ path.sep_str ++ "fmin.c",
+ "math" ++ path.sep_str ++ "fminf.c",
+ "math" ++ path.sep_str ++ "fminl.c",
+ "math" ++ path.sep_str ++ "fp_consts.c",
+ "math" ++ path.sep_str ++ "fp_constsf.c",
+ "math" ++ path.sep_str ++ "fp_constsl.c",
+ "math" ++ path.sep_str ++ "fpclassify.c",
+ "math" ++ path.sep_str ++ "fpclassifyf.c",
+ "math" ++ path.sep_str ++ "fpclassifyl.c",
+ "math" ++ path.sep_str ++ "frexpf.c",
+ "math" ++ path.sep_str ++ "hypot.c",
+ "math" ++ path.sep_str ++ "hypotf.c",
+ "math" ++ path.sep_str ++ "hypotl.c",
+ "math" ++ path.sep_str ++ "isnan.c",
+ "math" ++ path.sep_str ++ "isnanf.c",
+ "math" ++ path.sep_str ++ "isnanl.c",
+ "math" ++ path.sep_str ++ "ldexpf.c",
+ "math" ++ path.sep_str ++ "lgamma.c",
+ "math" ++ path.sep_str ++ "lgammaf.c",
+ "math" ++ path.sep_str ++ "lgammal.c",
+ "math" ++ path.sep_str ++ "llrint.c",
+ "math" ++ path.sep_str ++ "llrintf.c",
+ "math" ++ path.sep_str ++ "llrintl.c",
+ "math" ++ path.sep_str ++ "llround.c",
+ "math" ++ path.sep_str ++ "llroundf.c",
+ "math" ++ path.sep_str ++ "llroundl.c",
+ "math" ++ path.sep_str ++ "log10f.c",
+ "math" ++ path.sep_str ++ "logf.c",
+ "math" ++ path.sep_str ++ "lrint.c",
+ "math" ++ path.sep_str ++ "lrintf.c",
+ "math" ++ path.sep_str ++ "lrintl.c",
+ "math" ++ path.sep_str ++ "lround.c",
+ "math" ++ path.sep_str ++ "lroundf.c",
+ "math" ++ path.sep_str ++ "lroundl.c",
+ "math" ++ path.sep_str ++ "modf.c",
+ "math" ++ path.sep_str ++ "modff.c",
+ "math" ++ path.sep_str ++ "modfl.c",
+ "math" ++ path.sep_str ++ "nextafterf.c",
+ "math" ++ path.sep_str ++ "nextafterl.c",
+ "math" ++ path.sep_str ++ "nexttoward.c",
+ "math" ++ path.sep_str ++ "nexttowardf.c",
+ "math" ++ path.sep_str ++ "powf.c",
+ "math" ++ path.sep_str ++ "powi.c",
+ "math" ++ path.sep_str ++ "powif.c",
+ "math" ++ path.sep_str ++ "powil.c",
+ "math" ++ path.sep_str ++ "rint.c",
+ "math" ++ path.sep_str ++ "rintf.c",
+ "math" ++ path.sep_str ++ "rintl.c",
+ "math" ++ path.sep_str ++ "round.c",
+ "math" ++ path.sep_str ++ "roundf.c",
+ "math" ++ path.sep_str ++ "roundl.c",
+ "math" ++ path.sep_str ++ "s_erf.c",
+ "math" ++ path.sep_str ++ "sf_erf.c",
+ "math" ++ path.sep_str ++ "signbit.c",
+ "math" ++ path.sep_str ++ "signbitf.c",
+ "math" ++ path.sep_str ++ "signbitl.c",
+ "math" ++ path.sep_str ++ "signgam.c",
+ "math" ++ path.sep_str ++ "sinhf.c",
+ "math" ++ path.sep_str ++ "sinhl.c",
+ "math" ++ path.sep_str ++ "sqrt.c",
+ "math" ++ path.sep_str ++ "sqrtf.c",
+ "math" ++ path.sep_str ++ "sqrtl.c",
+ "math" ++ path.sep_str ++ "tanhf.c",
+ "math" ++ path.sep_str ++ "tanhl.c",
+ "math" ++ path.sep_str ++ "tgamma.c",
+ "math" ++ path.sep_str ++ "tgammaf.c",
+ "math" ++ path.sep_str ++ "tgammal.c",
+ "math" ++ path.sep_str ++ "truncl.c",
+ "misc" ++ path.sep_str ++ "alarm.c",
+ "misc" ++ path.sep_str ++ "basename.c",
+ "misc" ++ path.sep_str ++ "btowc.c",
+ "misc" ++ path.sep_str ++ "delay-f.c",
+ "misc" ++ path.sep_str ++ "delay-n.c",
+ "misc" ++ path.sep_str ++ "delayimp.c",
+ "misc" ++ path.sep_str ++ "dirent.c",
+ "misc" ++ path.sep_str ++ "dirname.c",
+ "misc" ++ path.sep_str ++ "feclearexcept.c",
+ "misc" ++ path.sep_str ++ "fegetenv.c",
+ "misc" ++ path.sep_str ++ "fegetexceptflag.c",
+ "misc" ++ path.sep_str ++ "fegetround.c",
+ "misc" ++ path.sep_str ++ "feholdexcept.c",
+ "misc" ++ path.sep_str ++ "feraiseexcept.c",
+ "misc" ++ path.sep_str ++ "fesetenv.c",
+ "misc" ++ path.sep_str ++ "fesetexceptflag.c",
+ "misc" ++ path.sep_str ++ "fesetround.c",
+ "misc" ++ path.sep_str ++ "fetestexcept.c",
+ "misc" ++ path.sep_str ++ "feupdateenv.c",
+ "misc" ++ path.sep_str ++ "ftruncate.c",
+ "misc" ++ path.sep_str ++ "ftw.c",
+ "misc" ++ path.sep_str ++ "ftw64.c",
+ "misc" ++ path.sep_str ++ "fwide.c",
+ "misc" ++ path.sep_str ++ "getlogin.c",
+ "misc" ++ path.sep_str ++ "getopt.c",
+ "misc" ++ path.sep_str ++ "gettimeofday.c",
+ "misc" ++ path.sep_str ++ "imaxabs.c",
+ "misc" ++ path.sep_str ++ "imaxdiv.c",
+ "misc" ++ path.sep_str ++ "isblank.c",
+ "misc" ++ path.sep_str ++ "iswblank.c",
+ "misc" ++ path.sep_str ++ "mbrtowc.c",
+ "misc" ++ path.sep_str ++ "mbsinit.c",
+ "misc" ++ path.sep_str ++ "mempcpy.c",
+ "misc" ++ path.sep_str ++ "mingw-aligned-malloc.c",
+ "misc" ++ path.sep_str ++ "mingw-fseek.c",
+ "misc" ++ path.sep_str ++ "mingw_getsp.S",
+ "misc" ++ path.sep_str ++ "mingw_matherr.c",
+ "misc" ++ path.sep_str ++ "mingw_mbwc_convert.c",
+ "misc" ++ path.sep_str ++ "mingw_usleep.c",
+ "misc" ++ path.sep_str ++ "mingw_wcstod.c",
+ "misc" ++ path.sep_str ++ "mingw_wcstof.c",
+ "misc" ++ path.sep_str ++ "mingw_wcstold.c",
+ "misc" ++ path.sep_str ++ "mkstemp.c",
+ "misc" ++ path.sep_str ++ "seterrno.c",
+ "misc" ++ path.sep_str ++ "sleep.c",
+ "misc" ++ path.sep_str ++ "strnlen.c",
+ "misc" ++ path.sep_str ++ "strsafe.c",
+ "misc" ++ path.sep_str ++ "strtoimax.c",
+ "misc" ++ path.sep_str ++ "strtold.c",
+ "misc" ++ path.sep_str ++ "strtoumax.c",
+ "misc" ++ path.sep_str ++ "tdelete.c",
+ "misc" ++ path.sep_str ++ "tfind.c",
+ "misc" ++ path.sep_str ++ "tsearch.c",
+ "misc" ++ path.sep_str ++ "twalk.c",
+ "misc" ++ path.sep_str ++ "uchar_c16rtomb.c",
+ "misc" ++ path.sep_str ++ "uchar_c32rtomb.c",
+ "misc" ++ path.sep_str ++ "uchar_mbrtoc16.c",
+ "misc" ++ path.sep_str ++ "uchar_mbrtoc32.c",
+ "misc" ++ path.sep_str ++ "wassert.c",
+ "misc" ++ path.sep_str ++ "wcrtomb.c",
+ "misc" ++ path.sep_str ++ "wcsnlen.c",
+ "misc" ++ path.sep_str ++ "wcstof.c",
+ "misc" ++ path.sep_str ++ "wcstoimax.c",
+ "misc" ++ path.sep_str ++ "wcstold.c",
+ "misc" ++ path.sep_str ++ "wcstoumax.c",
+ "misc" ++ path.sep_str ++ "wctob.c",
+ "misc" ++ path.sep_str ++ "wctrans.c",
+ "misc" ++ path.sep_str ++ "wctype.c",
+ "misc" ++ path.sep_str ++ "wdirent.c",
+ "misc" ++ path.sep_str ++ "winbs_uint64.c",
+ "misc" ++ path.sep_str ++ "winbs_ulong.c",
+ "misc" ++ path.sep_str ++ "winbs_ushort.c",
+ "misc" ++ path.sep_str ++ "wmemchr.c",
+ "misc" ++ path.sep_str ++ "wmemcmp.c",
+ "misc" ++ path.sep_str ++ "wmemcpy.c",
+ "misc" ++ path.sep_str ++ "wmemmove.c",
+ "misc" ++ path.sep_str ++ "wmempcpy.c",
+ "misc" ++ path.sep_str ++ "wmemset.c",
+ "stdio" ++ path.sep_str ++ "_Exit.c",
+ "stdio" ++ path.sep_str ++ "_findfirst64i32.c",
+ "stdio" ++ path.sep_str ++ "_findnext64i32.c",
+ "stdio" ++ path.sep_str ++ "_fstat.c",
+ "stdio" ++ path.sep_str ++ "_fstat64i32.c",
+ "stdio" ++ path.sep_str ++ "_ftime.c",
+ "stdio" ++ path.sep_str ++ "_getc_nolock.c",
+ "stdio" ++ path.sep_str ++ "_getwc_nolock.c",
+ "stdio" ++ path.sep_str ++ "_putc_nolock.c",
+ "stdio" ++ path.sep_str ++ "_putwc_nolock.c",
+ "stdio" ++ path.sep_str ++ "_stat.c",
+ "stdio" ++ path.sep_str ++ "_stat64i32.c",
+ "stdio" ++ path.sep_str ++ "_wfindfirst64i32.c",
+ "stdio" ++ path.sep_str ++ "_wfindnext64i32.c",
+ "stdio" ++ path.sep_str ++ "_wstat.c",
+ "stdio" ++ path.sep_str ++ "_wstat64i32.c",
+ "stdio" ++ path.sep_str ++ "asprintf.c",
+ "stdio" ++ path.sep_str ++ "atoll.c",
+ "stdio" ++ path.sep_str ++ "fgetpos64.c",
+ "stdio" ++ path.sep_str ++ "fopen64.c",
+ "stdio" ++ path.sep_str ++ "fseeko32.c",
+ "stdio" ++ path.sep_str ++ "fseeko64.c",
+ "stdio" ++ path.sep_str ++ "fsetpos64.c",
+ "stdio" ++ path.sep_str ++ "ftello.c",
+ "stdio" ++ path.sep_str ++ "ftello64.c",
+ "stdio" ++ path.sep_str ++ "ftruncate64.c",
+ "stdio" ++ path.sep_str ++ "lltoa.c",
+ "stdio" ++ path.sep_str ++ "lltow.c",
+ "stdio" ++ path.sep_str ++ "lseek64.c",
+ "stdio" ++ path.sep_str ++ "mingw_asprintf.c",
+ "stdio" ++ path.sep_str ++ "mingw_fprintf.c",
+ "stdio" ++ path.sep_str ++ "mingw_fprintfw.c",
+ "stdio" ++ path.sep_str ++ "mingw_fscanf.c",
+ "stdio" ++ path.sep_str ++ "mingw_fwscanf.c",
+ "stdio" ++ path.sep_str ++ "mingw_pformat.c",
+ "stdio" ++ path.sep_str ++ "mingw_pformatw.c",
+ "stdio" ++ path.sep_str ++ "mingw_printf.c",
+ "stdio" ++ path.sep_str ++ "mingw_printfw.c",
+ "stdio" ++ path.sep_str ++ "mingw_scanf.c",
+ "stdio" ++ path.sep_str ++ "mingw_snprintf.c",
+ "stdio" ++ path.sep_str ++ "mingw_snprintfw.c",
+ "stdio" ++ path.sep_str ++ "mingw_sprintf.c",
+ "stdio" ++ path.sep_str ++ "mingw_sprintfw.c",
+ "stdio" ++ path.sep_str ++ "mingw_sscanf.c",
+ "stdio" ++ path.sep_str ++ "mingw_swscanf.c",
+ "stdio" ++ path.sep_str ++ "mingw_vasprintf.c",
+ "stdio" ++ path.sep_str ++ "mingw_vfprintf.c",
+ "stdio" ++ path.sep_str ++ "mingw_vfprintfw.c",
+ "stdio" ++ path.sep_str ++ "mingw_vfscanf.c",
+ "stdio" ++ path.sep_str ++ "mingw_vprintf.c",
+ "stdio" ++ path.sep_str ++ "mingw_vprintfw.c",
+ "stdio" ++ path.sep_str ++ "mingw_vsnprintf.c",
+ "stdio" ++ path.sep_str ++ "mingw_vsnprintfw.c",
+ "stdio" ++ path.sep_str ++ "mingw_vsprintf.c",
+ "stdio" ++ path.sep_str ++ "mingw_vsprintfw.c",
+ "stdio" ++ path.sep_str ++ "mingw_wscanf.c",
+ "stdio" ++ path.sep_str ++ "mingw_wvfscanf.c",
+ "stdio" ++ path.sep_str ++ "scanf.S",
+ "stdio" ++ path.sep_str ++ "snprintf.c",
+ "stdio" ++ path.sep_str ++ "snwprintf.c",
+ "stdio" ++ path.sep_str ++ "strtof.c",
+ "stdio" ++ path.sep_str ++ "strtok_r.c",
+ "stdio" ++ path.sep_str ++ "truncate.c",
+ "stdio" ++ path.sep_str ++ "ulltoa.c",
+ "stdio" ++ path.sep_str ++ "ulltow.c",
+ "stdio" ++ path.sep_str ++ "vasprintf.c",
+ "stdio" ++ path.sep_str ++ "vfscanf.c",
+ "stdio" ++ path.sep_str ++ "vfscanf2.S",
+ "stdio" ++ path.sep_str ++ "vfwscanf.c",
+ "stdio" ++ path.sep_str ++ "vfwscanf2.S",
+ "stdio" ++ path.sep_str ++ "vscanf.c",
+ "stdio" ++ path.sep_str ++ "vscanf2.S",
+ "stdio" ++ path.sep_str ++ "vsnprintf.c",
+ "stdio" ++ path.sep_str ++ "vsnwprintf.c",
+ "stdio" ++ path.sep_str ++ "vsscanf.c",
+ "stdio" ++ path.sep_str ++ "vsscanf2.S",
+ "stdio" ++ path.sep_str ++ "vswscanf.c",
+ "stdio" ++ path.sep_str ++ "vswscanf2.S",
+ "stdio" ++ path.sep_str ++ "vwscanf.c",
+ "stdio" ++ path.sep_str ++ "vwscanf2.S",
+ "stdio" ++ path.sep_str ++ "wtoll.c",
+};
+
+const mingwex_x86_src = [_][]const u8{
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acosf.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acosh.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acoshf.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acoshl.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acosl.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "asinf.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "asinh.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "asinhf.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "asinhl.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "asinl.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atan2.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atan2f.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atan2l.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanf.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanh.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanhf.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanhl.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanl.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ceilf.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ceill.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ceil.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "_chgsignl.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "copysignl.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "cos.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "cosf.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "cosl.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "cosl_internal.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "cossin.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "exp2f.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "exp2l.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "exp2.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "exp.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expl.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expm1.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expm1f.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expm1l.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "floorf.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "floorl.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "floor.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmod.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmodf.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmodl.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fucom.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ilogbf.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ilogbl.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ilogb.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "internal_logl.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ldexp.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ldexpl.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log10l.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log1pf.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log1pl.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log1p.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log2f.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log2l.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log2.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "logb.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "logbf.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "logbl.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "logl.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "nearbyintf.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "nearbyintl.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "nearbyint.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "pow.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "powl.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remainderf.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remainderl.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remainder.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remquof.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remquol.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remquo.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "scalbnf.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "scalbnl.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "scalbn.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "sin.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "sinf.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "sinl.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "sinl_internal.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "tanf.c",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "tanl.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "truncf.S",
+ "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "trunc.S",
+};
+
+const mingwex_arm32_src = [_][]const u8{
+ "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "_chgsignl.S",
+ "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "exp2.c",
+ "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "nearbyint.S",
+ "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "nearbyintf.S",
+ "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "nearbyintl.S",
+ "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "trunc.S",
+ "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "truncf.S",
+};
+
+const mingwex_arm64_src = [_][]const u8{
+ "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "_chgsignl.S",
+ "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "exp2f.S",
+ "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "exp2.S",
+ "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "nearbyintf.S",
+ "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "nearbyintl.S",
+ "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "nearbyint.S",
+ "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "truncf.S",
+ "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "trunc.S",
+};
+
+const uuid_src = [_][]const u8{
+ "ativscp-uuid.c",
+ "atsmedia-uuid.c",
+ "bth-uuid.c",
+ "cguid-uuid.c",
+ "comcat-uuid.c",
+ "devguid.c",
+ "docobj-uuid.c",
+ "dxva-uuid.c",
+ "exdisp-uuid.c",
+ "extras-uuid.c",
+ "fwp-uuid.c",
+ "guid_nul.c",
+ "hlguids-uuid.c",
+ "hlink-uuid.c",
+ "mlang-uuid.c",
+ "msctf-uuid.c",
+ "mshtmhst-uuid.c",
+ "mshtml-uuid.c",
+ "msxml-uuid.c",
+ "netcon-uuid.c",
+ "ntddkbd-uuid.c",
+ "ntddmou-uuid.c",
+ "ntddpar-uuid.c",
+ "ntddscsi-uuid.c",
+ "ntddser-uuid.c",
+ "ntddstor-uuid.c",
+ "ntddvdeo-uuid.c",
+ "oaidl-uuid.c",
+ "objidl-uuid.c",
+ "objsafe-uuid.c",
+ "ocidl-uuid.c",
+ "oleacc-uuid.c",
+ "olectlid-uuid.c",
+ "oleidl-uuid.c",
+ "power-uuid.c",
+ "powrprof-uuid.c",
+ "uianimation-uuid.c",
+ "usbcamdi-uuid.c",
+ "usbiodef-uuid.c",
+ "uuid.c",
+ "vds-uuid.c",
+ "virtdisk-uuid.c",
+ "wia-uuid.c",
+};
+
+pub const always_link_libs = [_][]const u8{
+ "advapi32",
+ "kernel32",
+ "msvcrt",
+ "ntdll",
+ "shell32",
+ "user32",
+};
diff --git a/src/musl.zig b/src/musl.zig
new file mode 100644
index 0000000000..ef4ea7236b
--- /dev/null
+++ b/src/musl.zig
@@ -0,0 +1,2142 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const mem = std.mem;
+const path = std.fs.path;
+const assert = std.debug.assert;
+
+const target_util = @import("target.zig");
+const Compilation = @import("Compilation.zig");
+const build_options = @import("build_options");
+
+pub const CRTFile = enum {
+ crti_o,
+ crtn_o,
+ crt1_o,
+ scrt1_o,
+ libc_a,
+};
+
+pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
+ if (!build_options.have_llvm) {
+ return error.ZigCompilerNotBuiltWithLLVMExtensions;
+ }
+ const gpa = comp.gpa;
+ var arena_allocator = std.heap.ArenaAllocator.init(gpa);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ switch (crt_file) {
+ .crti_o => {
+ var args = std.ArrayList([]const u8).init(arena);
+ try add_cc_args(comp, arena, &args, false);
+ try args.appendSlice(&[_][]const u8{
+ "-Qunused-arguments",
+ });
+ return comp.build_crt_file("crti", .Obj, &[1]Compilation.CSourceFile{
+ .{
+ .src_path = try start_asm_path(comp, arena, "crti.s"),
+ .extra_flags = args.items,
+ },
+ });
+ },
+ .crtn_o => {
+ var args = std.ArrayList([]const u8).init(arena);
+ try add_cc_args(comp, arena, &args, false);
+ try args.appendSlice(&[_][]const u8{
+ "-Qunused-arguments",
+ });
+ return comp.build_crt_file("crtn", .Obj, &[1]Compilation.CSourceFile{
+ .{
+ .src_path = try start_asm_path(comp, arena, "crtn.s"),
+ .extra_flags = args.items,
+ },
+ });
+ },
+ .crt1_o => {
+ var args = std.ArrayList([]const u8).init(arena);
+ try add_cc_args(comp, arena, &args, false);
+ try args.appendSlice(&[_][]const u8{
+ "-fno-stack-protector",
+ "-DCRT",
+ });
+ return comp.build_crt_file("crt1", .Obj, &[1]Compilation.CSourceFile{
+ .{
+ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
+ "libc", "musl", "crt", "crt1.c",
+ }),
+ .extra_flags = args.items,
+ },
+ });
+ },
+ .scrt1_o => {
+ var args = std.ArrayList([]const u8).init(arena);
+ try add_cc_args(comp, arena, &args, false);
+ try args.appendSlice(&[_][]const u8{
+ "-fPIC",
+ "-fno-stack-protector",
+ "-DCRT",
+ });
+ return comp.build_crt_file("Scrt1", .Obj, &[1]Compilation.CSourceFile{
+ .{
+ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
+ "libc", "musl", "crt", "Scrt1.c",
+ }),
+ .extra_flags = args.items,
+ },
+ });
+ },
+ .libc_a => {
+ // When there is a src//foo.* then it should substitute for src/foo.*
+ // Even a .s file can substitute for a .c file.
+ const target = comp.getTarget();
+ const arch_name = target_util.archMuslName(target.cpu.arch);
+ var source_table = std.StringArrayHashMap(Ext).init(comp.gpa);
+ defer source_table.deinit();
+
+ try source_table.ensureCapacity(compat_time32_files.len + src_files.len);
+
+ for (src_files) |src_file| {
+ try addSrcFile(arena, &source_table, src_file);
+ }
+
+ const time32_compat_arch_list = [_][]const u8{ "arm", "i386", "mips", "powerpc" };
+ for (time32_compat_arch_list) |time32_compat_arch| {
+ if (mem.eql(u8, arch_name, time32_compat_arch)) {
+ for (compat_time32_files) |compat_time32_file| {
+ try addSrcFile(arena, &source_table, compat_time32_file);
+ }
+ }
+ }
+
+ var c_source_files = std.ArrayList(Compilation.CSourceFile).init(comp.gpa);
+ defer c_source_files.deinit();
+
+ var override_path = std.ArrayList(u8).init(comp.gpa);
+ defer override_path.deinit();
+
+ const s = path.sep_str;
+
+ for (source_table.items()) |entry| {
+ const src_file = entry.key;
+ const ext = entry.value;
+
+ const dirname = path.dirname(src_file).?;
+ const basename = path.basename(src_file);
+ const noextbasename = mem.split(basename, ".").next().?;
+ const before_arch_dir = path.dirname(dirname).?;
+ const dirbasename = path.basename(dirname);
+
+ var is_arch_specific = false;
+ // Architecture-specific implementations are under a / folder.
+ if (is_musl_arch_name(dirbasename)) {
+ if (!mem.eql(u8, dirbasename, arch_name))
+ continue; // Not the architecture we're compiling for.
+ is_arch_specific = true;
+ }
+ if (!is_arch_specific) {
+ // Look for an arch specific override.
+ override_path.shrinkRetainingCapacity(0);
+ try override_path.writer().print("{}" ++ s ++ "{}" ++ s ++ "{}.s", .{
+ dirname, arch_name, noextbasename,
+ });
+ if (source_table.contains(override_path.items))
+ continue;
+
+ override_path.shrinkRetainingCapacity(0);
+ try override_path.writer().print("{}" ++ s ++ "{}" ++ s ++ "{}.S", .{
+ dirname, arch_name, noextbasename,
+ });
+ if (source_table.contains(override_path.items))
+ continue;
+
+ override_path.shrinkRetainingCapacity(0);
+ try override_path.writer().print("{}" ++ s ++ "{}" ++ s ++ "{}.c", .{
+ dirname, arch_name, noextbasename,
+ });
+ if (source_table.contains(override_path.items))
+ continue;
+ }
+
+ var args = std.ArrayList([]const u8).init(arena);
+ try add_cc_args(comp, arena, &args, ext == .o3);
+ try args.appendSlice(&[_][]const u8{
+ "-Qunused-arguments",
+ "-w", // disable all warnings
+ });
+ const c_source_file = try c_source_files.addOne();
+ c_source_file.* = .{
+ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", src_file }),
+ .extra_flags = args.items,
+ };
+ }
+ return comp.build_crt_file("c", .Lib, c_source_files.items);
+ },
+ }
+}
+
+fn is_musl_arch_name(name: []const u8) bool {
+ const musl_arch_names = [_][]const u8{
+ "aarch64",
+ "arm",
+ "generic",
+ "i386",
+ "m68k",
+ "microblaze",
+ "mips",
+ "mips64",
+ "mipsn32",
+ "or1k",
+ "powerpc",
+ "powerpc64",
+ "riscv64",
+ "s390x",
+ "sh",
+ "x32",
+ "x86_64",
+ };
+ for (musl_arch_names) |musl_arch_name| {
+ if (mem.eql(u8, musl_arch_name, name)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+const Ext = enum {
+ assembly,
+ normal,
+ o3,
+};
+
+fn addSrcFile(arena: *Allocator, source_table: *std.StringArrayHashMap(Ext), file_path: []const u8) !void {
+ const ext: Ext = ext: {
+ if (mem.endsWith(u8, file_path, ".c")) {
+ if (mem.startsWith(u8, file_path, "musl/src/malloc/") or
+ mem.startsWith(u8, file_path, "musl/src/string/") or
+ mem.startsWith(u8, file_path, "musl/src/internal/"))
+ {
+ break :ext .o3;
+ } else {
+ break :ext .assembly;
+ }
+ } else if (mem.endsWith(u8, file_path, ".s") or mem.endsWith(u8, file_path, ".S")) {
+ break :ext .assembly;
+ } else {
+ unreachable;
+ }
+ };
+ // TODO do this at comptime on the comptime data rather than at runtime
+ // probably best to wait until self-hosted is done and our comptime execution
+ // is faster and uses less memory.
+ const key = if (path.sep != '/') blk: {
+ const mutable_file_path = try arena.dupe(u8, file_path);
+ for (mutable_file_path) |*c| {
+ if (c.* == '/') {
+ c.* = path.sep;
+ }
+ }
+ break :blk mutable_file_path;
+ } else file_path;
+ source_table.putAssumeCapacityNoClobber(key, ext);
+}
+
+fn add_cc_args(
+ comp: *Compilation,
+ arena: *Allocator,
+ args: *std.ArrayList([]const u8),
+ want_O3: bool,
+) error{OutOfMemory}!void {
+ const target = comp.getTarget();
+ const arch_name = target_util.archMuslName(target.cpu.arch);
+ const os_name = @tagName(target.os.tag);
+ const triple = try std.fmt.allocPrint(arena, "{}-{}-musl", .{ arch_name, os_name });
+ const o_arg = if (want_O3) "-O3" else "-Os";
+
+ try args.appendSlice(&[_][]const u8{
+ "-std=c99",
+ "-ffreestanding",
+ // Musl adds these args to builds with gcc but clang does not support them.
+ //"-fexcess-precision=standard",
+ //"-frounding-math",
+ "-Wa,--noexecstack",
+ "-D_XOPEN_SOURCE=700",
+
+ "-I",
+ try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "arch", arch_name }),
+
+ "-I",
+ try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "arch", "generic" }),
+
+ "-I",
+ try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "src", "include" }),
+
+ "-I",
+ try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "src", "internal" }),
+
+ "-I",
+ try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "include" }),
+
+ "-I",
+ try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "include", triple }),
+
+ "-I",
+ try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "include", "generic-musl" }),
+
+ o_arg,
+
+ "-fomit-frame-pointer",
+ "-fno-unwind-tables",
+ "-fno-asynchronous-unwind-tables",
+ "-ffunction-sections",
+ "-fdata-sections",
+ });
+}
+
+fn start_asm_path(comp: *Compilation, arena: *Allocator, basename: []const u8) ![]const u8 {
+ const target = comp.getTarget();
+ return comp.zig_lib_directory.join(arena, &[_][]const u8{
+ "libc", "musl", "crt", target_util.archMuslName(target.cpu.arch), basename,
+ });
+}
+
+const src_files = [_][]const u8{
+ "musl/src/aio/aio.c",
+ "musl/src/aio/aio_suspend.c",
+ "musl/src/aio/lio_listio.c",
+ "musl/src/complex/__cexp.c",
+ "musl/src/complex/__cexpf.c",
+ "musl/src/complex/cabs.c",
+ "musl/src/complex/cabsf.c",
+ "musl/src/complex/cabsl.c",
+ "musl/src/complex/cacos.c",
+ "musl/src/complex/cacosf.c",
+ "musl/src/complex/cacosh.c",
+ "musl/src/complex/cacoshf.c",
+ "musl/src/complex/cacoshl.c",
+ "musl/src/complex/cacosl.c",
+ "musl/src/complex/carg.c",
+ "musl/src/complex/cargf.c",
+ "musl/src/complex/cargl.c",
+ "musl/src/complex/casin.c",
+ "musl/src/complex/casinf.c",
+ "musl/src/complex/casinh.c",
+ "musl/src/complex/casinhf.c",
+ "musl/src/complex/casinhl.c",
+ "musl/src/complex/casinl.c",
+ "musl/src/complex/catan.c",
+ "musl/src/complex/catanf.c",
+ "musl/src/complex/catanh.c",
+ "musl/src/complex/catanhf.c",
+ "musl/src/complex/catanhl.c",
+ "musl/src/complex/catanl.c",
+ "musl/src/complex/ccos.c",
+ "musl/src/complex/ccosf.c",
+ "musl/src/complex/ccosh.c",
+ "musl/src/complex/ccoshf.c",
+ "musl/src/complex/ccoshl.c",
+ "musl/src/complex/ccosl.c",
+ "musl/src/complex/cexp.c",
+ "musl/src/complex/cexpf.c",
+ "musl/src/complex/cexpl.c",
+ "musl/src/complex/cimag.c",
+ "musl/src/complex/cimagf.c",
+ "musl/src/complex/cimagl.c",
+ "musl/src/complex/clog.c",
+ "musl/src/complex/clogf.c",
+ "musl/src/complex/clogl.c",
+ "musl/src/complex/conj.c",
+ "musl/src/complex/conjf.c",
+ "musl/src/complex/conjl.c",
+ "musl/src/complex/cpow.c",
+ "musl/src/complex/cpowf.c",
+ "musl/src/complex/cpowl.c",
+ "musl/src/complex/cproj.c",
+ "musl/src/complex/cprojf.c",
+ "musl/src/complex/cprojl.c",
+ "musl/src/complex/creal.c",
+ "musl/src/complex/crealf.c",
+ "musl/src/complex/creall.c",
+ "musl/src/complex/csin.c",
+ "musl/src/complex/csinf.c",
+ "musl/src/complex/csinh.c",
+ "musl/src/complex/csinhf.c",
+ "musl/src/complex/csinhl.c",
+ "musl/src/complex/csinl.c",
+ "musl/src/complex/csqrt.c",
+ "musl/src/complex/csqrtf.c",
+ "musl/src/complex/csqrtl.c",
+ "musl/src/complex/ctan.c",
+ "musl/src/complex/ctanf.c",
+ "musl/src/complex/ctanh.c",
+ "musl/src/complex/ctanhf.c",
+ "musl/src/complex/ctanhl.c",
+ "musl/src/complex/ctanl.c",
+ "musl/src/conf/confstr.c",
+ "musl/src/conf/fpathconf.c",
+ "musl/src/conf/legacy.c",
+ "musl/src/conf/pathconf.c",
+ "musl/src/conf/sysconf.c",
+ "musl/src/crypt/crypt.c",
+ "musl/src/crypt/crypt_blowfish.c",
+ "musl/src/crypt/crypt_des.c",
+ "musl/src/crypt/crypt_md5.c",
+ "musl/src/crypt/crypt_r.c",
+ "musl/src/crypt/crypt_sha256.c",
+ "musl/src/crypt/crypt_sha512.c",
+ "musl/src/crypt/encrypt.c",
+ "musl/src/ctype/__ctype_b_loc.c",
+ "musl/src/ctype/__ctype_get_mb_cur_max.c",
+ "musl/src/ctype/__ctype_tolower_loc.c",
+ "musl/src/ctype/__ctype_toupper_loc.c",
+ "musl/src/ctype/isalnum.c",
+ "musl/src/ctype/isalpha.c",
+ "musl/src/ctype/isascii.c",
+ "musl/src/ctype/isblank.c",
+ "musl/src/ctype/iscntrl.c",
+ "musl/src/ctype/isdigit.c",
+ "musl/src/ctype/isgraph.c",
+ "musl/src/ctype/islower.c",
+ "musl/src/ctype/isprint.c",
+ "musl/src/ctype/ispunct.c",
+ "musl/src/ctype/isspace.c",
+ "musl/src/ctype/isupper.c",
+ "musl/src/ctype/iswalnum.c",
+ "musl/src/ctype/iswalpha.c",
+ "musl/src/ctype/iswblank.c",
+ "musl/src/ctype/iswcntrl.c",
+ "musl/src/ctype/iswctype.c",
+ "musl/src/ctype/iswdigit.c",
+ "musl/src/ctype/iswgraph.c",
+ "musl/src/ctype/iswlower.c",
+ "musl/src/ctype/iswprint.c",
+ "musl/src/ctype/iswpunct.c",
+ "musl/src/ctype/iswspace.c",
+ "musl/src/ctype/iswupper.c",
+ "musl/src/ctype/iswxdigit.c",
+ "musl/src/ctype/isxdigit.c",
+ "musl/src/ctype/toascii.c",
+ "musl/src/ctype/tolower.c",
+ "musl/src/ctype/toupper.c",
+ "musl/src/ctype/towctrans.c",
+ "musl/src/ctype/wcswidth.c",
+ "musl/src/ctype/wctrans.c",
+ "musl/src/ctype/wcwidth.c",
+ "musl/src/dirent/alphasort.c",
+ "musl/src/dirent/closedir.c",
+ "musl/src/dirent/dirfd.c",
+ "musl/src/dirent/fdopendir.c",
+ "musl/src/dirent/opendir.c",
+ "musl/src/dirent/readdir.c",
+ "musl/src/dirent/readdir_r.c",
+ "musl/src/dirent/rewinddir.c",
+ "musl/src/dirent/scandir.c",
+ "musl/src/dirent/seekdir.c",
+ "musl/src/dirent/telldir.c",
+ "musl/src/dirent/versionsort.c",
+ "musl/src/env/__environ.c",
+ "musl/src/env/__init_tls.c",
+ "musl/src/env/__libc_start_main.c",
+ "musl/src/env/__reset_tls.c",
+ "musl/src/env/__stack_chk_fail.c",
+ "musl/src/env/clearenv.c",
+ "musl/src/env/getenv.c",
+ "musl/src/env/putenv.c",
+ "musl/src/env/secure_getenv.c",
+ "musl/src/env/setenv.c",
+ "musl/src/env/unsetenv.c",
+ "musl/src/errno/__errno_location.c",
+ "musl/src/errno/strerror.c",
+ "musl/src/exit/_Exit.c",
+ "musl/src/exit/abort.c",
+ "musl/src/exit/arm/__aeabi_atexit.c",
+ "musl/src/exit/assert.c",
+ "musl/src/exit/at_quick_exit.c",
+ "musl/src/exit/atexit.c",
+ "musl/src/exit/exit.c",
+ "musl/src/exit/quick_exit.c",
+ "musl/src/fcntl/creat.c",
+ "musl/src/fcntl/fcntl.c",
+ "musl/src/fcntl/open.c",
+ "musl/src/fcntl/openat.c",
+ "musl/src/fcntl/posix_fadvise.c",
+ "musl/src/fcntl/posix_fallocate.c",
+ "musl/src/fenv/__flt_rounds.c",
+ "musl/src/fenv/aarch64/fenv.s",
+ "musl/src/fenv/arm/fenv-hf.S",
+ "musl/src/fenv/arm/fenv.c",
+ "musl/src/fenv/fegetexceptflag.c",
+ "musl/src/fenv/feholdexcept.c",
+ "musl/src/fenv/fenv.c",
+ "musl/src/fenv/fesetexceptflag.c",
+ "musl/src/fenv/fesetround.c",
+ "musl/src/fenv/feupdateenv.c",
+ "musl/src/fenv/i386/fenv.s",
+ "musl/src/fenv/m68k/fenv.c",
+ "musl/src/fenv/mips/fenv-sf.c",
+ "musl/src/fenv/mips/fenv.S",
+ "musl/src/fenv/mips64/fenv-sf.c",
+ "musl/src/fenv/mips64/fenv.S",
+ "musl/src/fenv/mipsn32/fenv-sf.c",
+ "musl/src/fenv/mipsn32/fenv.S",
+ "musl/src/fenv/powerpc/fenv-sf.c",
+ "musl/src/fenv/powerpc/fenv.S",
+ "musl/src/fenv/powerpc64/fenv.c",
+ "musl/src/fenv/riscv64/fenv-sf.c",
+ "musl/src/fenv/riscv64/fenv.S",
+ "musl/src/fenv/s390x/fenv.c",
+ "musl/src/fenv/sh/fenv-nofpu.c",
+ "musl/src/fenv/sh/fenv.S",
+ "musl/src/fenv/x32/fenv.s",
+ "musl/src/fenv/x86_64/fenv.s",
+ "musl/src/internal/defsysinfo.c",
+ "musl/src/internal/floatscan.c",
+ "musl/src/internal/i386/defsysinfo.s",
+ "musl/src/internal/intscan.c",
+ "musl/src/internal/libc.c",
+ "musl/src/internal/procfdname.c",
+ "musl/src/internal/sh/__shcall.c",
+ "musl/src/internal/shgetc.c",
+ "musl/src/internal/syscall_ret.c",
+ "musl/src/internal/vdso.c",
+ "musl/src/internal/version.c",
+ "musl/src/ipc/ftok.c",
+ "musl/src/ipc/msgctl.c",
+ "musl/src/ipc/msgget.c",
+ "musl/src/ipc/msgrcv.c",
+ "musl/src/ipc/msgsnd.c",
+ "musl/src/ipc/semctl.c",
+ "musl/src/ipc/semget.c",
+ "musl/src/ipc/semop.c",
+ "musl/src/ipc/semtimedop.c",
+ "musl/src/ipc/shmat.c",
+ "musl/src/ipc/shmctl.c",
+ "musl/src/ipc/shmdt.c",
+ "musl/src/ipc/shmget.c",
+ "musl/src/ldso/__dlsym.c",
+ "musl/src/ldso/aarch64/dlsym.s",
+ "musl/src/ldso/aarch64/tlsdesc.s",
+ "musl/src/ldso/arm/dlsym.s",
+ "musl/src/ldso/arm/dlsym_time64.S",
+ "musl/src/ldso/arm/find_exidx.c",
+ "musl/src/ldso/arm/tlsdesc.S",
+ "musl/src/ldso/dl_iterate_phdr.c",
+ "musl/src/ldso/dladdr.c",
+ "musl/src/ldso/dlclose.c",
+ "musl/src/ldso/dlerror.c",
+ "musl/src/ldso/dlinfo.c",
+ "musl/src/ldso/dlopen.c",
+ "musl/src/ldso/dlsym.c",
+ "musl/src/ldso/i386/dlsym.s",
+ "musl/src/ldso/i386/dlsym_time64.S",
+ "musl/src/ldso/i386/tlsdesc.s",
+ "musl/src/ldso/m68k/dlsym.s",
+ "musl/src/ldso/m68k/dlsym_time64.S",
+ "musl/src/ldso/microblaze/dlsym.s",
+ "musl/src/ldso/microblaze/dlsym_time64.S",
+ "musl/src/ldso/mips/dlsym.s",
+ "musl/src/ldso/mips/dlsym_time64.S",
+ "musl/src/ldso/mips64/dlsym.s",
+ "musl/src/ldso/mipsn32/dlsym.s",
+ "musl/src/ldso/mipsn32/dlsym_time64.S",
+ "musl/src/ldso/or1k/dlsym.s",
+ "musl/src/ldso/or1k/dlsym_time64.S",
+ "musl/src/ldso/powerpc/dlsym.s",
+ "musl/src/ldso/powerpc/dlsym_time64.S",
+ "musl/src/ldso/powerpc64/dlsym.s",
+ "musl/src/ldso/riscv64/dlsym.s",
+ "musl/src/ldso/s390x/dlsym.s",
+ "musl/src/ldso/sh/dlsym.s",
+ "musl/src/ldso/sh/dlsym_time64.S",
+ "musl/src/ldso/tlsdesc.c",
+ "musl/src/ldso/x32/dlsym.s",
+ "musl/src/ldso/x86_64/dlsym.s",
+ "musl/src/ldso/x86_64/tlsdesc.s",
+ "musl/src/legacy/cuserid.c",
+ "musl/src/legacy/daemon.c",
+ "musl/src/legacy/err.c",
+ "musl/src/legacy/euidaccess.c",
+ "musl/src/legacy/ftw.c",
+ "musl/src/legacy/futimes.c",
+ "musl/src/legacy/getdtablesize.c",
+ "musl/src/legacy/getloadavg.c",
+ "musl/src/legacy/getpagesize.c",
+ "musl/src/legacy/getpass.c",
+ "musl/src/legacy/getusershell.c",
+ "musl/src/legacy/isastream.c",
+ "musl/src/legacy/lutimes.c",
+ "musl/src/legacy/ulimit.c",
+ "musl/src/legacy/utmpx.c",
+ "musl/src/legacy/valloc.c",
+ "musl/src/linux/adjtime.c",
+ "musl/src/linux/adjtimex.c",
+ "musl/src/linux/arch_prctl.c",
+ "musl/src/linux/brk.c",
+ "musl/src/linux/cache.c",
+ "musl/src/linux/cap.c",
+ "musl/src/linux/chroot.c",
+ "musl/src/linux/clock_adjtime.c",
+ "musl/src/linux/clone.c",
+ "musl/src/linux/copy_file_range.c",
+ "musl/src/linux/epoll.c",
+ "musl/src/linux/eventfd.c",
+ "musl/src/linux/fallocate.c",
+ "musl/src/linux/fanotify.c",
+ "musl/src/linux/flock.c",
+ "musl/src/linux/getdents.c",
+ "musl/src/linux/getrandom.c",
+ "musl/src/linux/inotify.c",
+ "musl/src/linux/ioperm.c",
+ "musl/src/linux/iopl.c",
+ "musl/src/linux/klogctl.c",
+ "musl/src/linux/membarrier.c",
+ "musl/src/linux/memfd_create.c",
+ "musl/src/linux/mlock2.c",
+ "musl/src/linux/module.c",
+ "musl/src/linux/mount.c",
+ "musl/src/linux/name_to_handle_at.c",
+ "musl/src/linux/open_by_handle_at.c",
+ "musl/src/linux/personality.c",
+ "musl/src/linux/pivot_root.c",
+ "musl/src/linux/ppoll.c",
+ "musl/src/linux/prctl.c",
+ "musl/src/linux/prlimit.c",
+ "musl/src/linux/process_vm.c",
+ "musl/src/linux/ptrace.c",
+ "musl/src/linux/quotactl.c",
+ "musl/src/linux/readahead.c",
+ "musl/src/linux/reboot.c",
+ "musl/src/linux/remap_file_pages.c",
+ "musl/src/linux/sbrk.c",
+ "musl/src/linux/sendfile.c",
+ "musl/src/linux/setfsgid.c",
+ "musl/src/linux/setfsuid.c",
+ "musl/src/linux/setgroups.c",
+ "musl/src/linux/sethostname.c",
+ "musl/src/linux/setns.c",
+ "musl/src/linux/settimeofday.c",
+ "musl/src/linux/signalfd.c",
+ "musl/src/linux/splice.c",
+ "musl/src/linux/stime.c",
+ "musl/src/linux/swap.c",
+ "musl/src/linux/sync_file_range.c",
+ "musl/src/linux/syncfs.c",
+ "musl/src/linux/sysinfo.c",
+ "musl/src/linux/tee.c",
+ "musl/src/linux/timerfd.c",
+ "musl/src/linux/unshare.c",
+ "musl/src/linux/utimes.c",
+ "musl/src/linux/vhangup.c",
+ "musl/src/linux/vmsplice.c",
+ "musl/src/linux/wait3.c",
+ "musl/src/linux/wait4.c",
+ "musl/src/linux/x32/sysinfo.c",
+ "musl/src/linux/xattr.c",
+ "musl/src/locale/__lctrans.c",
+ "musl/src/locale/__mo_lookup.c",
+ "musl/src/locale/bind_textdomain_codeset.c",
+ "musl/src/locale/c_locale.c",
+ "musl/src/locale/catclose.c",
+ "musl/src/locale/catgets.c",
+ "musl/src/locale/catopen.c",
+ "musl/src/locale/dcngettext.c",
+ "musl/src/locale/duplocale.c",
+ "musl/src/locale/freelocale.c",
+ "musl/src/locale/iconv.c",
+ "musl/src/locale/iconv_close.c",
+ "musl/src/locale/langinfo.c",
+ "musl/src/locale/locale_map.c",
+ "musl/src/locale/localeconv.c",
+ "musl/src/locale/newlocale.c",
+ "musl/src/locale/pleval.c",
+ "musl/src/locale/setlocale.c",
+ "musl/src/locale/strcoll.c",
+ "musl/src/locale/strfmon.c",
+ "musl/src/locale/strxfrm.c",
+ "musl/src/locale/textdomain.c",
+ "musl/src/locale/uselocale.c",
+ "musl/src/locale/wcscoll.c",
+ "musl/src/locale/wcsxfrm.c",
+ "musl/src/malloc/aligned_alloc.c",
+ "musl/src/malloc/expand_heap.c",
+ "musl/src/malloc/lite_malloc.c",
+ "musl/src/malloc/malloc.c",
+ "musl/src/malloc/malloc_usable_size.c",
+ "musl/src/malloc/memalign.c",
+ "musl/src/malloc/posix_memalign.c",
+ "musl/src/math/__cos.c",
+ "musl/src/math/__cosdf.c",
+ "musl/src/math/__cosl.c",
+ "musl/src/math/__expo2.c",
+ "musl/src/math/__expo2f.c",
+ "musl/src/math/__fpclassify.c",
+ "musl/src/math/__fpclassifyf.c",
+ "musl/src/math/__fpclassifyl.c",
+ "musl/src/math/__invtrigl.c",
+ "musl/src/math/__math_divzero.c",
+ "musl/src/math/__math_divzerof.c",
+ "musl/src/math/__math_invalid.c",
+ "musl/src/math/__math_invalidf.c",
+ "musl/src/math/__math_oflow.c",
+ "musl/src/math/__math_oflowf.c",
+ "musl/src/math/__math_uflow.c",
+ "musl/src/math/__math_uflowf.c",
+ "musl/src/math/__math_xflow.c",
+ "musl/src/math/__math_xflowf.c",
+ "musl/src/math/__polevll.c",
+ "musl/src/math/__rem_pio2.c",
+ "musl/src/math/__rem_pio2_large.c",
+ "musl/src/math/__rem_pio2f.c",
+ "musl/src/math/__rem_pio2l.c",
+ "musl/src/math/__signbit.c",
+ "musl/src/math/__signbitf.c",
+ "musl/src/math/__signbitl.c",
+ "musl/src/math/__sin.c",
+ "musl/src/math/__sindf.c",
+ "musl/src/math/__sinl.c",
+ "musl/src/math/__tan.c",
+ "musl/src/math/__tandf.c",
+ "musl/src/math/__tanl.c",
+ "musl/src/math/aarch64/ceil.c",
+ "musl/src/math/aarch64/ceilf.c",
+ "musl/src/math/aarch64/fabs.c",
+ "musl/src/math/aarch64/fabsf.c",
+ "musl/src/math/aarch64/floor.c",
+ "musl/src/math/aarch64/floorf.c",
+ "musl/src/math/aarch64/fma.c",
+ "musl/src/math/aarch64/fmaf.c",
+ "musl/src/math/aarch64/fmax.c",
+ "musl/src/math/aarch64/fmaxf.c",
+ "musl/src/math/aarch64/fmin.c",
+ "musl/src/math/aarch64/fminf.c",
+ "musl/src/math/aarch64/llrint.c",
+ "musl/src/math/aarch64/llrintf.c",
+ "musl/src/math/aarch64/llround.c",
+ "musl/src/math/aarch64/llroundf.c",
+ "musl/src/math/aarch64/lrint.c",
+ "musl/src/math/aarch64/lrintf.c",
+ "musl/src/math/aarch64/lround.c",
+ "musl/src/math/aarch64/lroundf.c",
+ "musl/src/math/aarch64/nearbyint.c",
+ "musl/src/math/aarch64/nearbyintf.c",
+ "musl/src/math/aarch64/rint.c",
+ "musl/src/math/aarch64/rintf.c",
+ "musl/src/math/aarch64/round.c",
+ "musl/src/math/aarch64/roundf.c",
+ "musl/src/math/aarch64/sqrt.c",
+ "musl/src/math/aarch64/sqrtf.c",
+ "musl/src/math/aarch64/trunc.c",
+ "musl/src/math/aarch64/truncf.c",
+ "musl/src/math/acos.c",
+ "musl/src/math/acosf.c",
+ "musl/src/math/acosh.c",
+ "musl/src/math/acoshf.c",
+ "musl/src/math/acoshl.c",
+ "musl/src/math/acosl.c",
+ "musl/src/math/arm/fabs.c",
+ "musl/src/math/arm/fabsf.c",
+ "musl/src/math/arm/fma.c",
+ "musl/src/math/arm/fmaf.c",
+ "musl/src/math/arm/sqrt.c",
+ "musl/src/math/arm/sqrtf.c",
+ "musl/src/math/asin.c",
+ "musl/src/math/asinf.c",
+ "musl/src/math/asinh.c",
+ "musl/src/math/asinhf.c",
+ "musl/src/math/asinhl.c",
+ "musl/src/math/asinl.c",
+ "musl/src/math/atan.c",
+ "musl/src/math/atan2.c",
+ "musl/src/math/atan2f.c",
+ "musl/src/math/atan2l.c",
+ "musl/src/math/atanf.c",
+ "musl/src/math/atanh.c",
+ "musl/src/math/atanhf.c",
+ "musl/src/math/atanhl.c",
+ "musl/src/math/atanl.c",
+ "musl/src/math/cbrt.c",
+ "musl/src/math/cbrtf.c",
+ "musl/src/math/cbrtl.c",
+ "musl/src/math/ceil.c",
+ "musl/src/math/ceilf.c",
+ "musl/src/math/ceill.c",
+ "musl/src/math/copysign.c",
+ "musl/src/math/copysignf.c",
+ "musl/src/math/copysignl.c",
+ "musl/src/math/cos.c",
+ "musl/src/math/cosf.c",
+ "musl/src/math/cosh.c",
+ "musl/src/math/coshf.c",
+ "musl/src/math/coshl.c",
+ "musl/src/math/cosl.c",
+ "musl/src/math/erf.c",
+ "musl/src/math/erff.c",
+ "musl/src/math/erfl.c",
+ "musl/src/math/exp.c",
+ "musl/src/math/exp10.c",
+ "musl/src/math/exp10f.c",
+ "musl/src/math/exp10l.c",
+ "musl/src/math/exp2.c",
+ "musl/src/math/exp2f.c",
+ "musl/src/math/exp2f_data.c",
+ "musl/src/math/exp2l.c",
+ "musl/src/math/exp_data.c",
+ "musl/src/math/expf.c",
+ "musl/src/math/expl.c",
+ "musl/src/math/expm1.c",
+ "musl/src/math/expm1f.c",
+ "musl/src/math/expm1l.c",
+ "musl/src/math/fabs.c",
+ "musl/src/math/fabsf.c",
+ "musl/src/math/fabsl.c",
+ "musl/src/math/fdim.c",
+ "musl/src/math/fdimf.c",
+ "musl/src/math/fdiml.c",
+ "musl/src/math/finite.c",
+ "musl/src/math/finitef.c",
+ "musl/src/math/floor.c",
+ "musl/src/math/floorf.c",
+ "musl/src/math/floorl.c",
+ "musl/src/math/fma.c",
+ "musl/src/math/fmaf.c",
+ "musl/src/math/fmal.c",
+ "musl/src/math/fmax.c",
+ "musl/src/math/fmaxf.c",
+ "musl/src/math/fmaxl.c",
+ "musl/src/math/fmin.c",
+ "musl/src/math/fminf.c",
+ "musl/src/math/fminl.c",
+ "musl/src/math/fmod.c",
+ "musl/src/math/fmodf.c",
+ "musl/src/math/fmodl.c",
+ "musl/src/math/frexp.c",
+ "musl/src/math/frexpf.c",
+ "musl/src/math/frexpl.c",
+ "musl/src/math/hypot.c",
+ "musl/src/math/hypotf.c",
+ "musl/src/math/hypotl.c",
+ "musl/src/math/i386/__invtrigl.s",
+ "musl/src/math/i386/acos.s",
+ "musl/src/math/i386/acosf.s",
+ "musl/src/math/i386/acosl.s",
+ "musl/src/math/i386/asin.s",
+ "musl/src/math/i386/asinf.s",
+ "musl/src/math/i386/asinl.s",
+ "musl/src/math/i386/atan.s",
+ "musl/src/math/i386/atan2.s",
+ "musl/src/math/i386/atan2f.s",
+ "musl/src/math/i386/atan2l.s",
+ "musl/src/math/i386/atanf.s",
+ "musl/src/math/i386/atanl.s",
+ "musl/src/math/i386/ceil.s",
+ "musl/src/math/i386/ceilf.s",
+ "musl/src/math/i386/ceill.s",
+ "musl/src/math/i386/exp2l.s",
+ "musl/src/math/i386/exp_ld.s",
+ "musl/src/math/i386/expl.s",
+ "musl/src/math/i386/expm1l.s",
+ "musl/src/math/i386/fabs.s",
+ "musl/src/math/i386/fabsf.s",
+ "musl/src/math/i386/fabsl.s",
+ "musl/src/math/i386/floor.s",
+ "musl/src/math/i386/floorf.s",
+ "musl/src/math/i386/floorl.s",
+ "musl/src/math/i386/fmod.s",
+ "musl/src/math/i386/fmodf.s",
+ "musl/src/math/i386/fmodl.s",
+ "musl/src/math/i386/hypot.s",
+ "musl/src/math/i386/hypotf.s",
+ "musl/src/math/i386/ldexp.s",
+ "musl/src/math/i386/ldexpf.s",
+ "musl/src/math/i386/ldexpl.s",
+ "musl/src/math/i386/llrint.s",
+ "musl/src/math/i386/llrintf.s",
+ "musl/src/math/i386/llrintl.s",
+ "musl/src/math/i386/log.s",
+ "musl/src/math/i386/log10.s",
+ "musl/src/math/i386/log10f.s",
+ "musl/src/math/i386/log10l.s",
+ "musl/src/math/i386/log1p.s",
+ "musl/src/math/i386/log1pf.s",
+ "musl/src/math/i386/log1pl.s",
+ "musl/src/math/i386/log2.s",
+ "musl/src/math/i386/log2f.s",
+ "musl/src/math/i386/log2l.s",
+ "musl/src/math/i386/logf.s",
+ "musl/src/math/i386/logl.s",
+ "musl/src/math/i386/lrint.s",
+ "musl/src/math/i386/lrintf.s",
+ "musl/src/math/i386/lrintl.s",
+ "musl/src/math/i386/remainder.s",
+ "musl/src/math/i386/remainderf.s",
+ "musl/src/math/i386/remainderl.s",
+ "musl/src/math/i386/remquo.s",
+ "musl/src/math/i386/remquof.s",
+ "musl/src/math/i386/remquol.s",
+ "musl/src/math/i386/rint.s",
+ "musl/src/math/i386/rintf.s",
+ "musl/src/math/i386/rintl.s",
+ "musl/src/math/i386/scalbln.s",
+ "musl/src/math/i386/scalblnf.s",
+ "musl/src/math/i386/scalblnl.s",
+ "musl/src/math/i386/scalbn.s",
+ "musl/src/math/i386/scalbnf.s",
+ "musl/src/math/i386/scalbnl.s",
+ "musl/src/math/i386/sqrt.s",
+ "musl/src/math/i386/sqrtf.s",
+ "musl/src/math/i386/sqrtl.s",
+ "musl/src/math/i386/trunc.s",
+ "musl/src/math/i386/truncf.s",
+ "musl/src/math/i386/truncl.s",
+ "musl/src/math/ilogb.c",
+ "musl/src/math/ilogbf.c",
+ "musl/src/math/ilogbl.c",
+ "musl/src/math/j0.c",
+ "musl/src/math/j0f.c",
+ "musl/src/math/j1.c",
+ "musl/src/math/j1f.c",
+ "musl/src/math/jn.c",
+ "musl/src/math/jnf.c",
+ "musl/src/math/ldexp.c",
+ "musl/src/math/ldexpf.c",
+ "musl/src/math/ldexpl.c",
+ "musl/src/math/lgamma.c",
+ "musl/src/math/lgamma_r.c",
+ "musl/src/math/lgammaf.c",
+ "musl/src/math/lgammaf_r.c",
+ "musl/src/math/lgammal.c",
+ "musl/src/math/llrint.c",
+ "musl/src/math/llrintf.c",
+ "musl/src/math/llrintl.c",
+ "musl/src/math/llround.c",
+ "musl/src/math/llroundf.c",
+ "musl/src/math/llroundl.c",
+ "musl/src/math/log.c",
+ "musl/src/math/log10.c",
+ "musl/src/math/log10f.c",
+ "musl/src/math/log10l.c",
+ "musl/src/math/log1p.c",
+ "musl/src/math/log1pf.c",
+ "musl/src/math/log1pl.c",
+ "musl/src/math/log2.c",
+ "musl/src/math/log2_data.c",
+ "musl/src/math/log2f.c",
+ "musl/src/math/log2f_data.c",
+ "musl/src/math/log2l.c",
+ "musl/src/math/log_data.c",
+ "musl/src/math/logb.c",
+ "musl/src/math/logbf.c",
+ "musl/src/math/logbl.c",
+ "musl/src/math/logf.c",
+ "musl/src/math/logf_data.c",
+ "musl/src/math/logl.c",
+ "musl/src/math/lrint.c",
+ "musl/src/math/lrintf.c",
+ "musl/src/math/lrintl.c",
+ "musl/src/math/lround.c",
+ "musl/src/math/lroundf.c",
+ "musl/src/math/lroundl.c",
+ "musl/src/math/mips/fabs.c",
+ "musl/src/math/mips/fabsf.c",
+ "musl/src/math/mips/sqrt.c",
+ "musl/src/math/mips/sqrtf.c",
+ "musl/src/math/modf.c",
+ "musl/src/math/modff.c",
+ "musl/src/math/modfl.c",
+ "musl/src/math/nan.c",
+ "musl/src/math/nanf.c",
+ "musl/src/math/nanl.c",
+ "musl/src/math/nearbyint.c",
+ "musl/src/math/nearbyintf.c",
+ "musl/src/math/nearbyintl.c",
+ "musl/src/math/nextafter.c",
+ "musl/src/math/nextafterf.c",
+ "musl/src/math/nextafterl.c",
+ "musl/src/math/nexttoward.c",
+ "musl/src/math/nexttowardf.c",
+ "musl/src/math/nexttowardl.c",
+ "musl/src/math/pow.c",
+ "musl/src/math/pow_data.c",
+ "musl/src/math/powerpc/fabs.c",
+ "musl/src/math/powerpc/fabsf.c",
+ "musl/src/math/powerpc/fma.c",
+ "musl/src/math/powerpc/fmaf.c",
+ "musl/src/math/powerpc/sqrt.c",
+ "musl/src/math/powerpc/sqrtf.c",
+ "musl/src/math/powerpc64/ceil.c",
+ "musl/src/math/powerpc64/ceilf.c",
+ "musl/src/math/powerpc64/fabs.c",
+ "musl/src/math/powerpc64/fabsf.c",
+ "musl/src/math/powerpc64/floor.c",
+ "musl/src/math/powerpc64/floorf.c",
+ "musl/src/math/powerpc64/fma.c",
+ "musl/src/math/powerpc64/fmaf.c",
+ "musl/src/math/powerpc64/fmax.c",
+ "musl/src/math/powerpc64/fmaxf.c",
+ "musl/src/math/powerpc64/fmin.c",
+ "musl/src/math/powerpc64/fminf.c",
+ "musl/src/math/powerpc64/lrint.c",
+ "musl/src/math/powerpc64/lrintf.c",
+ "musl/src/math/powerpc64/lround.c",
+ "musl/src/math/powerpc64/lroundf.c",
+ "musl/src/math/powerpc64/round.c",
+ "musl/src/math/powerpc64/roundf.c",
+ "musl/src/math/powerpc64/sqrt.c",
+ "musl/src/math/powerpc64/sqrtf.c",
+ "musl/src/math/powerpc64/trunc.c",
+ "musl/src/math/powerpc64/truncf.c",
+ "musl/src/math/powf.c",
+ "musl/src/math/powf_data.c",
+ "musl/src/math/powl.c",
+ "musl/src/math/remainder.c",
+ "musl/src/math/remainderf.c",
+ "musl/src/math/remainderl.c",
+ "musl/src/math/remquo.c",
+ "musl/src/math/remquof.c",
+ "musl/src/math/remquol.c",
+ "musl/src/math/rint.c",
+ "musl/src/math/rintf.c",
+ "musl/src/math/rintl.c",
+ "musl/src/math/riscv64/copysign.c",
+ "musl/src/math/riscv64/copysignf.c",
+ "musl/src/math/riscv64/fabs.c",
+ "musl/src/math/riscv64/fabsf.c",
+ "musl/src/math/riscv64/fma.c",
+ "musl/src/math/riscv64/fmaf.c",
+ "musl/src/math/riscv64/fmax.c",
+ "musl/src/math/riscv64/fmaxf.c",
+ "musl/src/math/riscv64/fmin.c",
+ "musl/src/math/riscv64/fminf.c",
+ "musl/src/math/riscv64/sqrt.c",
+ "musl/src/math/riscv64/sqrtf.c",
+ "musl/src/math/round.c",
+ "musl/src/math/roundf.c",
+ "musl/src/math/roundl.c",
+ "musl/src/math/s390x/ceil.c",
+ "musl/src/math/s390x/ceilf.c",
+ "musl/src/math/s390x/ceill.c",
+ "musl/src/math/s390x/fabs.c",
+ "musl/src/math/s390x/fabsf.c",
+ "musl/src/math/s390x/fabsl.c",
+ "musl/src/math/s390x/floor.c",
+ "musl/src/math/s390x/floorf.c",
+ "musl/src/math/s390x/floorl.c",
+ "musl/src/math/s390x/fma.c",
+ "musl/src/math/s390x/fmaf.c",
+ "musl/src/math/s390x/nearbyint.c",
+ "musl/src/math/s390x/nearbyintf.c",
+ "musl/src/math/s390x/nearbyintl.c",
+ "musl/src/math/s390x/rint.c",
+ "musl/src/math/s390x/rintf.c",
+ "musl/src/math/s390x/rintl.c",
+ "musl/src/math/s390x/round.c",
+ "musl/src/math/s390x/roundf.c",
+ "musl/src/math/s390x/roundl.c",
+ "musl/src/math/s390x/sqrt.c",
+ "musl/src/math/s390x/sqrtf.c",
+ "musl/src/math/s390x/sqrtl.c",
+ "musl/src/math/s390x/trunc.c",
+ "musl/src/math/s390x/truncf.c",
+ "musl/src/math/s390x/truncl.c",
+ "musl/src/math/scalb.c",
+ "musl/src/math/scalbf.c",
+ "musl/src/math/scalbln.c",
+ "musl/src/math/scalblnf.c",
+ "musl/src/math/scalblnl.c",
+ "musl/src/math/scalbn.c",
+ "musl/src/math/scalbnf.c",
+ "musl/src/math/scalbnl.c",
+ "musl/src/math/signgam.c",
+ "musl/src/math/significand.c",
+ "musl/src/math/significandf.c",
+ "musl/src/math/sin.c",
+ "musl/src/math/sincos.c",
+ "musl/src/math/sincosf.c",
+ "musl/src/math/sincosl.c",
+ "musl/src/math/sinf.c",
+ "musl/src/math/sinh.c",
+ "musl/src/math/sinhf.c",
+ "musl/src/math/sinhl.c",
+ "musl/src/math/sinl.c",
+ "musl/src/math/sqrt.c",
+ "musl/src/math/sqrtf.c",
+ "musl/src/math/sqrtl.c",
+ "musl/src/math/tan.c",
+ "musl/src/math/tanf.c",
+ "musl/src/math/tanh.c",
+ "musl/src/math/tanhf.c",
+ "musl/src/math/tanhl.c",
+ "musl/src/math/tanl.c",
+ "musl/src/math/tgamma.c",
+ "musl/src/math/tgammaf.c",
+ "musl/src/math/tgammal.c",
+ "musl/src/math/trunc.c",
+ "musl/src/math/truncf.c",
+ "musl/src/math/truncl.c",
+ "musl/src/math/x32/__invtrigl.s",
+ "musl/src/math/x32/acosl.s",
+ "musl/src/math/x32/asinl.s",
+ "musl/src/math/x32/atan2l.s",
+ "musl/src/math/x32/atanl.s",
+ "musl/src/math/x32/ceill.s",
+ "musl/src/math/x32/exp2l.s",
+ "musl/src/math/x32/expl.s",
+ "musl/src/math/x32/expm1l.s",
+ "musl/src/math/x32/fabs.s",
+ "musl/src/math/x32/fabsf.s",
+ "musl/src/math/x32/fabsl.s",
+ "musl/src/math/x32/floorl.s",
+ "musl/src/math/x32/fma.c",
+ "musl/src/math/x32/fmaf.c",
+ "musl/src/math/x32/fmodl.s",
+ "musl/src/math/x32/llrint.s",
+ "musl/src/math/x32/llrintf.s",
+ "musl/src/math/x32/llrintl.s",
+ "musl/src/math/x32/log10l.s",
+ "musl/src/math/x32/log1pl.s",
+ "musl/src/math/x32/log2l.s",
+ "musl/src/math/x32/logl.s",
+ "musl/src/math/x32/lrint.s",
+ "musl/src/math/x32/lrintf.s",
+ "musl/src/math/x32/lrintl.s",
+ "musl/src/math/x32/remainderl.s",
+ "musl/src/math/x32/rintl.s",
+ "musl/src/math/x32/sqrt.s",
+ "musl/src/math/x32/sqrtf.s",
+ "musl/src/math/x32/sqrtl.s",
+ "musl/src/math/x32/truncl.s",
+ "musl/src/math/x86_64/__invtrigl.s",
+ "musl/src/math/x86_64/acosl.s",
+ "musl/src/math/x86_64/asinl.s",
+ "musl/src/math/x86_64/atan2l.s",
+ "musl/src/math/x86_64/atanl.s",
+ "musl/src/math/x86_64/ceill.s",
+ "musl/src/math/x86_64/exp2l.s",
+ "musl/src/math/x86_64/expl.s",
+ "musl/src/math/x86_64/expm1l.s",
+ "musl/src/math/x86_64/fabs.s",
+ "musl/src/math/x86_64/fabsf.s",
+ "musl/src/math/x86_64/fabsl.s",
+ "musl/src/math/x86_64/floorl.s",
+ "musl/src/math/x86_64/fma.c",
+ "musl/src/math/x86_64/fmaf.c",
+ "musl/src/math/x86_64/fmodl.s",
+ "musl/src/math/x86_64/llrint.s",
+ "musl/src/math/x86_64/llrintf.s",
+ "musl/src/math/x86_64/llrintl.s",
+ "musl/src/math/x86_64/log10l.s",
+ "musl/src/math/x86_64/log1pl.s",
+ "musl/src/math/x86_64/log2l.s",
+ "musl/src/math/x86_64/logl.s",
+ "musl/src/math/x86_64/lrint.s",
+ "musl/src/math/x86_64/lrintf.s",
+ "musl/src/math/x86_64/lrintl.s",
+ "musl/src/math/x86_64/remainderl.s",
+ "musl/src/math/x86_64/rintl.s",
+ "musl/src/math/x86_64/sqrt.s",
+ "musl/src/math/x86_64/sqrtf.s",
+ "musl/src/math/x86_64/sqrtl.s",
+ "musl/src/math/x86_64/truncl.s",
+ "musl/src/misc/a64l.c",
+ "musl/src/misc/basename.c",
+ "musl/src/misc/dirname.c",
+ "musl/src/misc/ffs.c",
+ "musl/src/misc/ffsl.c",
+ "musl/src/misc/ffsll.c",
+ "musl/src/misc/fmtmsg.c",
+ "musl/src/misc/forkpty.c",
+ "musl/src/misc/get_current_dir_name.c",
+ "musl/src/misc/getauxval.c",
+ "musl/src/misc/getdomainname.c",
+ "musl/src/misc/getentropy.c",
+ "musl/src/misc/gethostid.c",
+ "musl/src/misc/getopt.c",
+ "musl/src/misc/getopt_long.c",
+ "musl/src/misc/getpriority.c",
+ "musl/src/misc/getresgid.c",
+ "musl/src/misc/getresuid.c",
+ "musl/src/misc/getrlimit.c",
+ "musl/src/misc/getrusage.c",
+ "musl/src/misc/getsubopt.c",
+ "musl/src/misc/initgroups.c",
+ "musl/src/misc/ioctl.c",
+ "musl/src/misc/issetugid.c",
+ "musl/src/misc/lockf.c",
+ "musl/src/misc/login_tty.c",
+ "musl/src/misc/mntent.c",
+ "musl/src/misc/nftw.c",
+ "musl/src/misc/openpty.c",
+ "musl/src/misc/ptsname.c",
+ "musl/src/misc/pty.c",
+ "musl/src/misc/realpath.c",
+ "musl/src/misc/setdomainname.c",
+ "musl/src/misc/setpriority.c",
+ "musl/src/misc/setrlimit.c",
+ "musl/src/misc/syscall.c",
+ "musl/src/misc/syslog.c",
+ "musl/src/misc/uname.c",
+ "musl/src/misc/wordexp.c",
+ "musl/src/mman/madvise.c",
+ "musl/src/mman/mincore.c",
+ "musl/src/mman/mlock.c",
+ "musl/src/mman/mlockall.c",
+ "musl/src/mman/mmap.c",
+ "musl/src/mman/mprotect.c",
+ "musl/src/mman/mremap.c",
+ "musl/src/mman/msync.c",
+ "musl/src/mman/munlock.c",
+ "musl/src/mman/munlockall.c",
+ "musl/src/mman/munmap.c",
+ "musl/src/mman/posix_madvise.c",
+ "musl/src/mman/shm_open.c",
+ "musl/src/mq/mq_close.c",
+ "musl/src/mq/mq_getattr.c",
+ "musl/src/mq/mq_notify.c",
+ "musl/src/mq/mq_open.c",
+ "musl/src/mq/mq_receive.c",
+ "musl/src/mq/mq_send.c",
+ "musl/src/mq/mq_setattr.c",
+ "musl/src/mq/mq_timedreceive.c",
+ "musl/src/mq/mq_timedsend.c",
+ "musl/src/mq/mq_unlink.c",
+ "musl/src/multibyte/btowc.c",
+ "musl/src/multibyte/c16rtomb.c",
+ "musl/src/multibyte/c32rtomb.c",
+ "musl/src/multibyte/internal.c",
+ "musl/src/multibyte/mblen.c",
+ "musl/src/multibyte/mbrlen.c",
+ "musl/src/multibyte/mbrtoc16.c",
+ "musl/src/multibyte/mbrtoc32.c",
+ "musl/src/multibyte/mbrtowc.c",
+ "musl/src/multibyte/mbsinit.c",
+ "musl/src/multibyte/mbsnrtowcs.c",
+ "musl/src/multibyte/mbsrtowcs.c",
+ "musl/src/multibyte/mbstowcs.c",
+ "musl/src/multibyte/mbtowc.c",
+ "musl/src/multibyte/wcrtomb.c",
+ "musl/src/multibyte/wcsnrtombs.c",
+ "musl/src/multibyte/wcsrtombs.c",
+ "musl/src/multibyte/wcstombs.c",
+ "musl/src/multibyte/wctob.c",
+ "musl/src/multibyte/wctomb.c",
+ "musl/src/network/accept.c",
+ "musl/src/network/accept4.c",
+ "musl/src/network/bind.c",
+ "musl/src/network/connect.c",
+ "musl/src/network/dn_comp.c",
+ "musl/src/network/dn_expand.c",
+ "musl/src/network/dn_skipname.c",
+ "musl/src/network/dns_parse.c",
+ "musl/src/network/ent.c",
+ "musl/src/network/ether.c",
+ "musl/src/network/freeaddrinfo.c",
+ "musl/src/network/gai_strerror.c",
+ "musl/src/network/getaddrinfo.c",
+ "musl/src/network/gethostbyaddr.c",
+ "musl/src/network/gethostbyaddr_r.c",
+ "musl/src/network/gethostbyname.c",
+ "musl/src/network/gethostbyname2.c",
+ "musl/src/network/gethostbyname2_r.c",
+ "musl/src/network/gethostbyname_r.c",
+ "musl/src/network/getifaddrs.c",
+ "musl/src/network/getnameinfo.c",
+ "musl/src/network/getpeername.c",
+ "musl/src/network/getservbyname.c",
+ "musl/src/network/getservbyname_r.c",
+ "musl/src/network/getservbyport.c",
+ "musl/src/network/getservbyport_r.c",
+ "musl/src/network/getsockname.c",
+ "musl/src/network/getsockopt.c",
+ "musl/src/network/h_errno.c",
+ "musl/src/network/herror.c",
+ "musl/src/network/hstrerror.c",
+ "musl/src/network/htonl.c",
+ "musl/src/network/htons.c",
+ "musl/src/network/if_freenameindex.c",
+ "musl/src/network/if_indextoname.c",
+ "musl/src/network/if_nameindex.c",
+ "musl/src/network/if_nametoindex.c",
+ "musl/src/network/in6addr_any.c",
+ "musl/src/network/in6addr_loopback.c",
+ "musl/src/network/inet_addr.c",
+ "musl/src/network/inet_aton.c",
+ "musl/src/network/inet_legacy.c",
+ "musl/src/network/inet_ntoa.c",
+ "musl/src/network/inet_ntop.c",
+ "musl/src/network/inet_pton.c",
+ "musl/src/network/listen.c",
+ "musl/src/network/lookup_ipliteral.c",
+ "musl/src/network/lookup_name.c",
+ "musl/src/network/lookup_serv.c",
+ "musl/src/network/netlink.c",
+ "musl/src/network/netname.c",
+ "musl/src/network/ns_parse.c",
+ "musl/src/network/ntohl.c",
+ "musl/src/network/ntohs.c",
+ "musl/src/network/proto.c",
+ "musl/src/network/recv.c",
+ "musl/src/network/recvfrom.c",
+ "musl/src/network/recvmmsg.c",
+ "musl/src/network/recvmsg.c",
+ "musl/src/network/res_init.c",
+ "musl/src/network/res_mkquery.c",
+ "musl/src/network/res_msend.c",
+ "musl/src/network/res_query.c",
+ "musl/src/network/res_querydomain.c",
+ "musl/src/network/res_send.c",
+ "musl/src/network/res_state.c",
+ "musl/src/network/resolvconf.c",
+ "musl/src/network/send.c",
+ "musl/src/network/sendmmsg.c",
+ "musl/src/network/sendmsg.c",
+ "musl/src/network/sendto.c",
+ "musl/src/network/serv.c",
+ "musl/src/network/setsockopt.c",
+ "musl/src/network/shutdown.c",
+ "musl/src/network/sockatmark.c",
+ "musl/src/network/socket.c",
+ "musl/src/network/socketpair.c",
+ "musl/src/passwd/fgetgrent.c",
+ "musl/src/passwd/fgetpwent.c",
+ "musl/src/passwd/fgetspent.c",
+ "musl/src/passwd/getgr_a.c",
+ "musl/src/passwd/getgr_r.c",
+ "musl/src/passwd/getgrent.c",
+ "musl/src/passwd/getgrent_a.c",
+ "musl/src/passwd/getgrouplist.c",
+ "musl/src/passwd/getpw_a.c",
+ "musl/src/passwd/getpw_r.c",
+ "musl/src/passwd/getpwent.c",
+ "musl/src/passwd/getpwent_a.c",
+ "musl/src/passwd/getspent.c",
+ "musl/src/passwd/getspnam.c",
+ "musl/src/passwd/getspnam_r.c",
+ "musl/src/passwd/lckpwdf.c",
+ "musl/src/passwd/nscd_query.c",
+ "musl/src/passwd/putgrent.c",
+ "musl/src/passwd/putpwent.c",
+ "musl/src/passwd/putspent.c",
+ "musl/src/prng/__rand48_step.c",
+ "musl/src/prng/__seed48.c",
+ "musl/src/prng/drand48.c",
+ "musl/src/prng/lcong48.c",
+ "musl/src/prng/lrand48.c",
+ "musl/src/prng/mrand48.c",
+ "musl/src/prng/rand.c",
+ "musl/src/prng/rand_r.c",
+ "musl/src/prng/random.c",
+ "musl/src/prng/seed48.c",
+ "musl/src/prng/srand48.c",
+ "musl/src/process/arm/vfork.s",
+ "musl/src/process/execl.c",
+ "musl/src/process/execle.c",
+ "musl/src/process/execlp.c",
+ "musl/src/process/execv.c",
+ "musl/src/process/execve.c",
+ "musl/src/process/execvp.c",
+ "musl/src/process/fexecve.c",
+ "musl/src/process/fork.c",
+ "musl/src/process/i386/vfork.s",
+ "musl/src/process/posix_spawn.c",
+ "musl/src/process/posix_spawn_file_actions_addchdir.c",
+ "musl/src/process/posix_spawn_file_actions_addclose.c",
+ "musl/src/process/posix_spawn_file_actions_adddup2.c",
+ "musl/src/process/posix_spawn_file_actions_addfchdir.c",
+ "musl/src/process/posix_spawn_file_actions_addopen.c",
+ "musl/src/process/posix_spawn_file_actions_destroy.c",
+ "musl/src/process/posix_spawn_file_actions_init.c",
+ "musl/src/process/posix_spawnattr_destroy.c",
+ "musl/src/process/posix_spawnattr_getflags.c",
+ "musl/src/process/posix_spawnattr_getpgroup.c",
+ "musl/src/process/posix_spawnattr_getsigdefault.c",
+ "musl/src/process/posix_spawnattr_getsigmask.c",
+ "musl/src/process/posix_spawnattr_init.c",
+ "musl/src/process/posix_spawnattr_sched.c",
+ "musl/src/process/posix_spawnattr_setflags.c",
+ "musl/src/process/posix_spawnattr_setpgroup.c",
+ "musl/src/process/posix_spawnattr_setsigdefault.c",
+ "musl/src/process/posix_spawnattr_setsigmask.c",
+ "musl/src/process/posix_spawnp.c",
+ "musl/src/process/s390x/vfork.s",
+ "musl/src/process/sh/vfork.s",
+ "musl/src/process/system.c",
+ "musl/src/process/vfork.c",
+ "musl/src/process/wait.c",
+ "musl/src/process/waitid.c",
+ "musl/src/process/waitpid.c",
+ "musl/src/process/x32/vfork.s",
+ "musl/src/process/x86_64/vfork.s",
+ "musl/src/regex/fnmatch.c",
+ "musl/src/regex/glob.c",
+ "musl/src/regex/regcomp.c",
+ "musl/src/regex/regerror.c",
+ "musl/src/regex/regexec.c",
+ "musl/src/regex/tre-mem.c",
+ "musl/src/sched/affinity.c",
+ "musl/src/sched/sched_cpucount.c",
+ "musl/src/sched/sched_get_priority_max.c",
+ "musl/src/sched/sched_getcpu.c",
+ "musl/src/sched/sched_getparam.c",
+ "musl/src/sched/sched_getscheduler.c",
+ "musl/src/sched/sched_rr_get_interval.c",
+ "musl/src/sched/sched_setparam.c",
+ "musl/src/sched/sched_setscheduler.c",
+ "musl/src/sched/sched_yield.c",
+ "musl/src/search/hsearch.c",
+ "musl/src/search/insque.c",
+ "musl/src/search/lsearch.c",
+ "musl/src/search/tdelete.c",
+ "musl/src/search/tdestroy.c",
+ "musl/src/search/tfind.c",
+ "musl/src/search/tsearch.c",
+ "musl/src/search/twalk.c",
+ "musl/src/select/poll.c",
+ "musl/src/select/pselect.c",
+ "musl/src/select/select.c",
+ "musl/src/setjmp/aarch64/longjmp.s",
+ "musl/src/setjmp/aarch64/setjmp.s",
+ "musl/src/setjmp/arm/longjmp.S",
+ "musl/src/setjmp/arm/setjmp.S",
+ "musl/src/setjmp/i386/longjmp.s",
+ "musl/src/setjmp/i386/setjmp.s",
+ "musl/src/setjmp/longjmp.c",
+ "musl/src/setjmp/m68k/longjmp.s",
+ "musl/src/setjmp/m68k/setjmp.s",
+ "musl/src/setjmp/microblaze/longjmp.s",
+ "musl/src/setjmp/microblaze/setjmp.s",
+ "musl/src/setjmp/mips/longjmp.S",
+ "musl/src/setjmp/mips/setjmp.S",
+ "musl/src/setjmp/mips64/longjmp.S",
+ "musl/src/setjmp/mips64/setjmp.S",
+ "musl/src/setjmp/mipsn32/longjmp.S",
+ "musl/src/setjmp/mipsn32/setjmp.S",
+ "musl/src/setjmp/or1k/longjmp.s",
+ "musl/src/setjmp/or1k/setjmp.s",
+ "musl/src/setjmp/powerpc/longjmp.S",
+ "musl/src/setjmp/powerpc/setjmp.S",
+ "musl/src/setjmp/powerpc64/longjmp.s",
+ "musl/src/setjmp/powerpc64/setjmp.s",
+ "musl/src/setjmp/riscv64/longjmp.S",
+ "musl/src/setjmp/riscv64/setjmp.S",
+ "musl/src/setjmp/s390x/longjmp.s",
+ "musl/src/setjmp/s390x/setjmp.s",
+ "musl/src/setjmp/setjmp.c",
+ "musl/src/setjmp/sh/longjmp.S",
+ "musl/src/setjmp/sh/setjmp.S",
+ "musl/src/setjmp/x32/longjmp.s",
+ "musl/src/setjmp/x32/setjmp.s",
+ "musl/src/setjmp/x86_64/longjmp.s",
+ "musl/src/setjmp/x86_64/setjmp.s",
+ "musl/src/signal/aarch64/restore.s",
+ "musl/src/signal/aarch64/sigsetjmp.s",
+ "musl/src/signal/arm/restore.s",
+ "musl/src/signal/arm/sigsetjmp.s",
+ "musl/src/signal/block.c",
+ "musl/src/signal/getitimer.c",
+ "musl/src/signal/i386/restore.s",
+ "musl/src/signal/i386/sigsetjmp.s",
+ "musl/src/signal/kill.c",
+ "musl/src/signal/killpg.c",
+ "musl/src/signal/m68k/sigsetjmp.s",
+ "musl/src/signal/microblaze/restore.s",
+ "musl/src/signal/microblaze/sigsetjmp.s",
+ "musl/src/signal/mips/restore.s",
+ "musl/src/signal/mips/sigsetjmp.s",
+ "musl/src/signal/mips64/restore.s",
+ "musl/src/signal/mips64/sigsetjmp.s",
+ "musl/src/signal/mipsn32/restore.s",
+ "musl/src/signal/mipsn32/sigsetjmp.s",
+ "musl/src/signal/or1k/sigsetjmp.s",
+ "musl/src/signal/powerpc/restore.s",
+ "musl/src/signal/powerpc/sigsetjmp.s",
+ "musl/src/signal/powerpc64/restore.s",
+ "musl/src/signal/powerpc64/sigsetjmp.s",
+ "musl/src/signal/psiginfo.c",
+ "musl/src/signal/psignal.c",
+ "musl/src/signal/raise.c",
+ "musl/src/signal/restore.c",
+ "musl/src/signal/riscv64/restore.s",
+ "musl/src/signal/riscv64/sigsetjmp.s",
+ "musl/src/signal/s390x/restore.s",
+ "musl/src/signal/s390x/sigsetjmp.s",
+ "musl/src/signal/setitimer.c",
+ "musl/src/signal/sh/restore.s",
+ "musl/src/signal/sh/sigsetjmp.s",
+ "musl/src/signal/sigaction.c",
+ "musl/src/signal/sigaddset.c",
+ "musl/src/signal/sigaltstack.c",
+ "musl/src/signal/sigandset.c",
+ "musl/src/signal/sigdelset.c",
+ "musl/src/signal/sigemptyset.c",
+ "musl/src/signal/sigfillset.c",
+ "musl/src/signal/sighold.c",
+ "musl/src/signal/sigignore.c",
+ "musl/src/signal/siginterrupt.c",
+ "musl/src/signal/sigisemptyset.c",
+ "musl/src/signal/sigismember.c",
+ "musl/src/signal/siglongjmp.c",
+ "musl/src/signal/signal.c",
+ "musl/src/signal/sigorset.c",
+ "musl/src/signal/sigpause.c",
+ "musl/src/signal/sigpending.c",
+ "musl/src/signal/sigprocmask.c",
+ "musl/src/signal/sigqueue.c",
+ "musl/src/signal/sigrelse.c",
+ "musl/src/signal/sigrtmax.c",
+ "musl/src/signal/sigrtmin.c",
+ "musl/src/signal/sigset.c",
+ "musl/src/signal/sigsetjmp.c",
+ "musl/src/signal/sigsetjmp_tail.c",
+ "musl/src/signal/sigsuspend.c",
+ "musl/src/signal/sigtimedwait.c",
+ "musl/src/signal/sigwait.c",
+ "musl/src/signal/sigwaitinfo.c",
+ "musl/src/signal/x32/getitimer.c",
+ "musl/src/signal/x32/restore.s",
+ "musl/src/signal/x32/setitimer.c",
+ "musl/src/signal/x32/sigsetjmp.s",
+ "musl/src/signal/x86_64/restore.s",
+ "musl/src/signal/x86_64/sigsetjmp.s",
+ "musl/src/stat/__xstat.c",
+ "musl/src/stat/chmod.c",
+ "musl/src/stat/fchmod.c",
+ "musl/src/stat/fchmodat.c",
+ "musl/src/stat/fstat.c",
+ "musl/src/stat/fstatat.c",
+ "musl/src/stat/futimens.c",
+ "musl/src/stat/futimesat.c",
+ "musl/src/stat/lchmod.c",
+ "musl/src/stat/lstat.c",
+ "musl/src/stat/mkdir.c",
+ "musl/src/stat/mkdirat.c",
+ "musl/src/stat/mkfifo.c",
+ "musl/src/stat/mkfifoat.c",
+ "musl/src/stat/mknod.c",
+ "musl/src/stat/mknodat.c",
+ "musl/src/stat/stat.c",
+ "musl/src/stat/statvfs.c",
+ "musl/src/stat/umask.c",
+ "musl/src/stat/utimensat.c",
+ "musl/src/stdio/__fclose_ca.c",
+ "musl/src/stdio/__fdopen.c",
+ "musl/src/stdio/__fmodeflags.c",
+ "musl/src/stdio/__fopen_rb_ca.c",
+ "musl/src/stdio/__lockfile.c",
+ "musl/src/stdio/__overflow.c",
+ "musl/src/stdio/__stdio_close.c",
+ "musl/src/stdio/__stdio_exit.c",
+ "musl/src/stdio/__stdio_read.c",
+ "musl/src/stdio/__stdio_seek.c",
+ "musl/src/stdio/__stdio_write.c",
+ "musl/src/stdio/__stdout_write.c",
+ "musl/src/stdio/__string_read.c",
+ "musl/src/stdio/__toread.c",
+ "musl/src/stdio/__towrite.c",
+ "musl/src/stdio/__uflow.c",
+ "musl/src/stdio/asprintf.c",
+ "musl/src/stdio/clearerr.c",
+ "musl/src/stdio/dprintf.c",
+ "musl/src/stdio/ext.c",
+ "musl/src/stdio/ext2.c",
+ "musl/src/stdio/fclose.c",
+ "musl/src/stdio/feof.c",
+ "musl/src/stdio/ferror.c",
+ "musl/src/stdio/fflush.c",
+ "musl/src/stdio/fgetc.c",
+ "musl/src/stdio/fgetln.c",
+ "musl/src/stdio/fgetpos.c",
+ "musl/src/stdio/fgets.c",
+ "musl/src/stdio/fgetwc.c",
+ "musl/src/stdio/fgetws.c",
+ "musl/src/stdio/fileno.c",
+ "musl/src/stdio/flockfile.c",
+ "musl/src/stdio/fmemopen.c",
+ "musl/src/stdio/fopen.c",
+ "musl/src/stdio/fopencookie.c",
+ "musl/src/stdio/fprintf.c",
+ "musl/src/stdio/fputc.c",
+ "musl/src/stdio/fputs.c",
+ "musl/src/stdio/fputwc.c",
+ "musl/src/stdio/fputws.c",
+ "musl/src/stdio/fread.c",
+ "musl/src/stdio/freopen.c",
+ "musl/src/stdio/fscanf.c",
+ "musl/src/stdio/fseek.c",
+ "musl/src/stdio/fsetpos.c",
+ "musl/src/stdio/ftell.c",
+ "musl/src/stdio/ftrylockfile.c",
+ "musl/src/stdio/funlockfile.c",
+ "musl/src/stdio/fwide.c",
+ "musl/src/stdio/fwprintf.c",
+ "musl/src/stdio/fwrite.c",
+ "musl/src/stdio/fwscanf.c",
+ "musl/src/stdio/getc.c",
+ "musl/src/stdio/getc_unlocked.c",
+ "musl/src/stdio/getchar.c",
+ "musl/src/stdio/getchar_unlocked.c",
+ "musl/src/stdio/getdelim.c",
+ "musl/src/stdio/getline.c",
+ "musl/src/stdio/gets.c",
+ "musl/src/stdio/getw.c",
+ "musl/src/stdio/getwc.c",
+ "musl/src/stdio/getwchar.c",
+ "musl/src/stdio/ofl.c",
+ "musl/src/stdio/ofl_add.c",
+ "musl/src/stdio/open_memstream.c",
+ "musl/src/stdio/open_wmemstream.c",
+ "musl/src/stdio/pclose.c",
+ "musl/src/stdio/perror.c",
+ "musl/src/stdio/popen.c",
+ "musl/src/stdio/printf.c",
+ "musl/src/stdio/putc.c",
+ "musl/src/stdio/putc_unlocked.c",
+ "musl/src/stdio/putchar.c",
+ "musl/src/stdio/putchar_unlocked.c",
+ "musl/src/stdio/puts.c",
+ "musl/src/stdio/putw.c",
+ "musl/src/stdio/putwc.c",
+ "musl/src/stdio/putwchar.c",
+ "musl/src/stdio/remove.c",
+ "musl/src/stdio/rename.c",
+ "musl/src/stdio/rewind.c",
+ "musl/src/stdio/scanf.c",
+ "musl/src/stdio/setbuf.c",
+ "musl/src/stdio/setbuffer.c",
+ "musl/src/stdio/setlinebuf.c",
+ "musl/src/stdio/setvbuf.c",
+ "musl/src/stdio/snprintf.c",
+ "musl/src/stdio/sprintf.c",
+ "musl/src/stdio/sscanf.c",
+ "musl/src/stdio/stderr.c",
+ "musl/src/stdio/stdin.c",
+ "musl/src/stdio/stdout.c",
+ "musl/src/stdio/swprintf.c",
+ "musl/src/stdio/swscanf.c",
+ "musl/src/stdio/tempnam.c",
+ "musl/src/stdio/tmpfile.c",
+ "musl/src/stdio/tmpnam.c",
+ "musl/src/stdio/ungetc.c",
+ "musl/src/stdio/ungetwc.c",
+ "musl/src/stdio/vasprintf.c",
+ "musl/src/stdio/vdprintf.c",
+ "musl/src/stdio/vfprintf.c",
+ "musl/src/stdio/vfscanf.c",
+ "musl/src/stdio/vfwprintf.c",
+ "musl/src/stdio/vfwscanf.c",
+ "musl/src/stdio/vprintf.c",
+ "musl/src/stdio/vscanf.c",
+ "musl/src/stdio/vsnprintf.c",
+ "musl/src/stdio/vsprintf.c",
+ "musl/src/stdio/vsscanf.c",
+ "musl/src/stdio/vswprintf.c",
+ "musl/src/stdio/vswscanf.c",
+ "musl/src/stdio/vwprintf.c",
+ "musl/src/stdio/vwscanf.c",
+ "musl/src/stdio/wprintf.c",
+ "musl/src/stdio/wscanf.c",
+ "musl/src/stdlib/abs.c",
+ "musl/src/stdlib/atof.c",
+ "musl/src/stdlib/atoi.c",
+ "musl/src/stdlib/atol.c",
+ "musl/src/stdlib/atoll.c",
+ "musl/src/stdlib/bsearch.c",
+ "musl/src/stdlib/div.c",
+ "musl/src/stdlib/ecvt.c",
+ "musl/src/stdlib/fcvt.c",
+ "musl/src/stdlib/gcvt.c",
+ "musl/src/stdlib/imaxabs.c",
+ "musl/src/stdlib/imaxdiv.c",
+ "musl/src/stdlib/labs.c",
+ "musl/src/stdlib/ldiv.c",
+ "musl/src/stdlib/llabs.c",
+ "musl/src/stdlib/lldiv.c",
+ "musl/src/stdlib/qsort.c",
+ "musl/src/stdlib/strtod.c",
+ "musl/src/stdlib/strtol.c",
+ "musl/src/stdlib/wcstod.c",
+ "musl/src/stdlib/wcstol.c",
+ "musl/src/string/arm/__aeabi_memcpy.s",
+ "musl/src/string/arm/__aeabi_memset.s",
+ "musl/src/string/arm/memcpy.c",
+ "musl/src/string/arm/memcpy_le.S",
+ "musl/src/string/bcmp.c",
+ "musl/src/string/bcopy.c",
+ "musl/src/string/bzero.c",
+ "musl/src/string/explicit_bzero.c",
+ "musl/src/string/i386/memcpy.s",
+ "musl/src/string/i386/memmove.s",
+ "musl/src/string/i386/memset.s",
+ "musl/src/string/index.c",
+ "musl/src/string/memccpy.c",
+ "musl/src/string/memchr.c",
+ "musl/src/string/memcmp.c",
+ "musl/src/string/memcpy.c",
+ "musl/src/string/memmem.c",
+ "musl/src/string/memmove.c",
+ "musl/src/string/mempcpy.c",
+ "musl/src/string/memrchr.c",
+ "musl/src/string/memset.c",
+ "musl/src/string/rindex.c",
+ "musl/src/string/stpcpy.c",
+ "musl/src/string/stpncpy.c",
+ "musl/src/string/strcasecmp.c",
+ "musl/src/string/strcasestr.c",
+ "musl/src/string/strcat.c",
+ "musl/src/string/strchr.c",
+ "musl/src/string/strchrnul.c",
+ "musl/src/string/strcmp.c",
+ "musl/src/string/strcpy.c",
+ "musl/src/string/strcspn.c",
+ "musl/src/string/strdup.c",
+ "musl/src/string/strerror_r.c",
+ "musl/src/string/strlcat.c",
+ "musl/src/string/strlcpy.c",
+ "musl/src/string/strlen.c",
+ "musl/src/string/strncasecmp.c",
+ "musl/src/string/strncat.c",
+ "musl/src/string/strncmp.c",
+ "musl/src/string/strncpy.c",
+ "musl/src/string/strndup.c",
+ "musl/src/string/strnlen.c",
+ "musl/src/string/strpbrk.c",
+ "musl/src/string/strrchr.c",
+ "musl/src/string/strsep.c",
+ "musl/src/string/strsignal.c",
+ "musl/src/string/strspn.c",
+ "musl/src/string/strstr.c",
+ "musl/src/string/strtok.c",
+ "musl/src/string/strtok_r.c",
+ "musl/src/string/strverscmp.c",
+ "musl/src/string/swab.c",
+ "musl/src/string/wcpcpy.c",
+ "musl/src/string/wcpncpy.c",
+ "musl/src/string/wcscasecmp.c",
+ "musl/src/string/wcscasecmp_l.c",
+ "musl/src/string/wcscat.c",
+ "musl/src/string/wcschr.c",
+ "musl/src/string/wcscmp.c",
+ "musl/src/string/wcscpy.c",
+ "musl/src/string/wcscspn.c",
+ "musl/src/string/wcsdup.c",
+ "musl/src/string/wcslen.c",
+ "musl/src/string/wcsncasecmp.c",
+ "musl/src/string/wcsncasecmp_l.c",
+ "musl/src/string/wcsncat.c",
+ "musl/src/string/wcsncmp.c",
+ "musl/src/string/wcsncpy.c",
+ "musl/src/string/wcsnlen.c",
+ "musl/src/string/wcspbrk.c",
+ "musl/src/string/wcsrchr.c",
+ "musl/src/string/wcsspn.c",
+ "musl/src/string/wcsstr.c",
+ "musl/src/string/wcstok.c",
+ "musl/src/string/wcswcs.c",
+ "musl/src/string/wmemchr.c",
+ "musl/src/string/wmemcmp.c",
+ "musl/src/string/wmemcpy.c",
+ "musl/src/string/wmemmove.c",
+ "musl/src/string/wmemset.c",
+ "musl/src/string/x86_64/memcpy.s",
+ "musl/src/string/x86_64/memmove.s",
+ "musl/src/string/x86_64/memset.s",
+ "musl/src/temp/__randname.c",
+ "musl/src/temp/mkdtemp.c",
+ "musl/src/temp/mkostemp.c",
+ "musl/src/temp/mkostemps.c",
+ "musl/src/temp/mkstemp.c",
+ "musl/src/temp/mkstemps.c",
+ "musl/src/temp/mktemp.c",
+ "musl/src/termios/cfgetospeed.c",
+ "musl/src/termios/cfmakeraw.c",
+ "musl/src/termios/cfsetospeed.c",
+ "musl/src/termios/tcdrain.c",
+ "musl/src/termios/tcflow.c",
+ "musl/src/termios/tcflush.c",
+ "musl/src/termios/tcgetattr.c",
+ "musl/src/termios/tcgetsid.c",
+ "musl/src/termios/tcsendbreak.c",
+ "musl/src/termios/tcsetattr.c",
+ "musl/src/thread/__lock.c",
+ "musl/src/thread/__set_thread_area.c",
+ "musl/src/thread/__syscall_cp.c",
+ "musl/src/thread/__timedwait.c",
+ "musl/src/thread/__tls_get_addr.c",
+ "musl/src/thread/__unmapself.c",
+ "musl/src/thread/__wait.c",
+ "musl/src/thread/aarch64/__set_thread_area.s",
+ "musl/src/thread/aarch64/__unmapself.s",
+ "musl/src/thread/aarch64/clone.s",
+ "musl/src/thread/aarch64/syscall_cp.s",
+ "musl/src/thread/arm/__aeabi_read_tp.s",
+ "musl/src/thread/arm/__set_thread_area.c",
+ "musl/src/thread/arm/__unmapself.s",
+ "musl/src/thread/arm/atomics.s",
+ "musl/src/thread/arm/clone.s",
+ "musl/src/thread/arm/syscall_cp.s",
+ "musl/src/thread/call_once.c",
+ "musl/src/thread/clone.c",
+ "musl/src/thread/cnd_broadcast.c",
+ "musl/src/thread/cnd_destroy.c",
+ "musl/src/thread/cnd_init.c",
+ "musl/src/thread/cnd_signal.c",
+ "musl/src/thread/cnd_timedwait.c",
+ "musl/src/thread/cnd_wait.c",
+ "musl/src/thread/default_attr.c",
+ "musl/src/thread/i386/__set_thread_area.s",
+ "musl/src/thread/i386/__unmapself.s",
+ "musl/src/thread/i386/clone.s",
+ "musl/src/thread/i386/syscall_cp.s",
+ "musl/src/thread/i386/tls.s",
+ "musl/src/thread/lock_ptc.c",
+ "musl/src/thread/m68k/__m68k_read_tp.s",
+ "musl/src/thread/m68k/clone.s",
+ "musl/src/thread/m68k/syscall_cp.s",
+ "musl/src/thread/microblaze/__set_thread_area.s",
+ "musl/src/thread/microblaze/__unmapself.s",
+ "musl/src/thread/microblaze/clone.s",
+ "musl/src/thread/microblaze/syscall_cp.s",
+ "musl/src/thread/mips/__unmapself.s",
+ "musl/src/thread/mips/clone.s",
+ "musl/src/thread/mips/syscall_cp.s",
+ "musl/src/thread/mips64/__unmapself.s",
+ "musl/src/thread/mips64/clone.s",
+ "musl/src/thread/mips64/syscall_cp.s",
+ "musl/src/thread/mipsn32/__unmapself.s",
+ "musl/src/thread/mipsn32/clone.s",
+ "musl/src/thread/mipsn32/syscall_cp.s",
+ "musl/src/thread/mtx_destroy.c",
+ "musl/src/thread/mtx_init.c",
+ "musl/src/thread/mtx_lock.c",
+ "musl/src/thread/mtx_timedlock.c",
+ "musl/src/thread/mtx_trylock.c",
+ "musl/src/thread/mtx_unlock.c",
+ "musl/src/thread/or1k/__set_thread_area.s",
+ "musl/src/thread/or1k/__unmapself.s",
+ "musl/src/thread/or1k/clone.s",
+ "musl/src/thread/or1k/syscall_cp.s",
+ "musl/src/thread/powerpc/__set_thread_area.s",
+ "musl/src/thread/powerpc/__unmapself.s",
+ "musl/src/thread/powerpc/clone.s",
+ "musl/src/thread/powerpc/syscall_cp.s",
+ "musl/src/thread/powerpc64/__set_thread_area.s",
+ "musl/src/thread/powerpc64/__unmapself.s",
+ "musl/src/thread/powerpc64/clone.s",
+ "musl/src/thread/powerpc64/syscall_cp.s",
+ "musl/src/thread/pthread_atfork.c",
+ "musl/src/thread/pthread_attr_destroy.c",
+ "musl/src/thread/pthread_attr_get.c",
+ "musl/src/thread/pthread_attr_init.c",
+ "musl/src/thread/pthread_attr_setdetachstate.c",
+ "musl/src/thread/pthread_attr_setguardsize.c",
+ "musl/src/thread/pthread_attr_setinheritsched.c",
+ "musl/src/thread/pthread_attr_setschedparam.c",
+ "musl/src/thread/pthread_attr_setschedpolicy.c",
+ "musl/src/thread/pthread_attr_setscope.c",
+ "musl/src/thread/pthread_attr_setstack.c",
+ "musl/src/thread/pthread_attr_setstacksize.c",
+ "musl/src/thread/pthread_barrier_destroy.c",
+ "musl/src/thread/pthread_barrier_init.c",
+ "musl/src/thread/pthread_barrier_wait.c",
+ "musl/src/thread/pthread_barrierattr_destroy.c",
+ "musl/src/thread/pthread_barrierattr_init.c",
+ "musl/src/thread/pthread_barrierattr_setpshared.c",
+ "musl/src/thread/pthread_cancel.c",
+ "musl/src/thread/pthread_cleanup_push.c",
+ "musl/src/thread/pthread_cond_broadcast.c",
+ "musl/src/thread/pthread_cond_destroy.c",
+ "musl/src/thread/pthread_cond_init.c",
+ "musl/src/thread/pthread_cond_signal.c",
+ "musl/src/thread/pthread_cond_timedwait.c",
+ "musl/src/thread/pthread_cond_wait.c",
+ "musl/src/thread/pthread_condattr_destroy.c",
+ "musl/src/thread/pthread_condattr_init.c",
+ "musl/src/thread/pthread_condattr_setclock.c",
+ "musl/src/thread/pthread_condattr_setpshared.c",
+ "musl/src/thread/pthread_create.c",
+ "musl/src/thread/pthread_detach.c",
+ "musl/src/thread/pthread_equal.c",
+ "musl/src/thread/pthread_getattr_np.c",
+ "musl/src/thread/pthread_getconcurrency.c",
+ "musl/src/thread/pthread_getcpuclockid.c",
+ "musl/src/thread/pthread_getschedparam.c",
+ "musl/src/thread/pthread_getspecific.c",
+ "musl/src/thread/pthread_join.c",
+ "musl/src/thread/pthread_key_create.c",
+ "musl/src/thread/pthread_kill.c",
+ "musl/src/thread/pthread_mutex_consistent.c",
+ "musl/src/thread/pthread_mutex_destroy.c",
+ "musl/src/thread/pthread_mutex_getprioceiling.c",
+ "musl/src/thread/pthread_mutex_init.c",
+ "musl/src/thread/pthread_mutex_lock.c",
+ "musl/src/thread/pthread_mutex_setprioceiling.c",
+ "musl/src/thread/pthread_mutex_timedlock.c",
+ "musl/src/thread/pthread_mutex_trylock.c",
+ "musl/src/thread/pthread_mutex_unlock.c",
+ "musl/src/thread/pthread_mutexattr_destroy.c",
+ "musl/src/thread/pthread_mutexattr_init.c",
+ "musl/src/thread/pthread_mutexattr_setprotocol.c",
+ "musl/src/thread/pthread_mutexattr_setpshared.c",
+ "musl/src/thread/pthread_mutexattr_setrobust.c",
+ "musl/src/thread/pthread_mutexattr_settype.c",
+ "musl/src/thread/pthread_once.c",
+ "musl/src/thread/pthread_rwlock_destroy.c",
+ "musl/src/thread/pthread_rwlock_init.c",
+ "musl/src/thread/pthread_rwlock_rdlock.c",
+ "musl/src/thread/pthread_rwlock_timedrdlock.c",
+ "musl/src/thread/pthread_rwlock_timedwrlock.c",
+ "musl/src/thread/pthread_rwlock_tryrdlock.c",
+ "musl/src/thread/pthread_rwlock_trywrlock.c",
+ "musl/src/thread/pthread_rwlock_unlock.c",
+ "musl/src/thread/pthread_rwlock_wrlock.c",
+ "musl/src/thread/pthread_rwlockattr_destroy.c",
+ "musl/src/thread/pthread_rwlockattr_init.c",
+ "musl/src/thread/pthread_rwlockattr_setpshared.c",
+ "musl/src/thread/pthread_self.c",
+ "musl/src/thread/pthread_setattr_default_np.c",
+ "musl/src/thread/pthread_setcancelstate.c",
+ "musl/src/thread/pthread_setcanceltype.c",
+ "musl/src/thread/pthread_setconcurrency.c",
+ "musl/src/thread/pthread_setname_np.c",
+ "musl/src/thread/pthread_setschedparam.c",
+ "musl/src/thread/pthread_setschedprio.c",
+ "musl/src/thread/pthread_setspecific.c",
+ "musl/src/thread/pthread_sigmask.c",
+ "musl/src/thread/pthread_spin_destroy.c",
+ "musl/src/thread/pthread_spin_init.c",
+ "musl/src/thread/pthread_spin_lock.c",
+ "musl/src/thread/pthread_spin_trylock.c",
+ "musl/src/thread/pthread_spin_unlock.c",
+ "musl/src/thread/pthread_testcancel.c",
+ "musl/src/thread/riscv64/__set_thread_area.s",
+ "musl/src/thread/riscv64/__unmapself.s",
+ "musl/src/thread/riscv64/clone.s",
+ "musl/src/thread/riscv64/syscall_cp.s",
+ "musl/src/thread/s390x/__set_thread_area.s",
+ "musl/src/thread/s390x/__tls_get_offset.s",
+ "musl/src/thread/s390x/__unmapself.s",
+ "musl/src/thread/s390x/clone.s",
+ "musl/src/thread/s390x/syscall_cp.s",
+ "musl/src/thread/sem_destroy.c",
+ "musl/src/thread/sem_getvalue.c",
+ "musl/src/thread/sem_init.c",
+ "musl/src/thread/sem_open.c",
+ "musl/src/thread/sem_post.c",
+ "musl/src/thread/sem_timedwait.c",
+ "musl/src/thread/sem_trywait.c",
+ "musl/src/thread/sem_unlink.c",
+ "musl/src/thread/sem_wait.c",
+ "musl/src/thread/sh/__set_thread_area.c",
+ "musl/src/thread/sh/__unmapself.c",
+ "musl/src/thread/sh/__unmapself_mmu.s",
+ "musl/src/thread/sh/atomics.s",
+ "musl/src/thread/sh/clone.s",
+ "musl/src/thread/sh/syscall_cp.s",
+ "musl/src/thread/synccall.c",
+ "musl/src/thread/syscall_cp.c",
+ "musl/src/thread/thrd_create.c",
+ "musl/src/thread/thrd_exit.c",
+ "musl/src/thread/thrd_join.c",
+ "musl/src/thread/thrd_sleep.c",
+ "musl/src/thread/thrd_yield.c",
+ "musl/src/thread/tls.c",
+ "musl/src/thread/tss_create.c",
+ "musl/src/thread/tss_delete.c",
+ "musl/src/thread/tss_set.c",
+ "musl/src/thread/vmlock.c",
+ "musl/src/thread/x32/__set_thread_area.s",
+ "musl/src/thread/x32/__unmapself.s",
+ "musl/src/thread/x32/clone.s",
+ "musl/src/thread/x32/syscall_cp.s",
+ "musl/src/thread/x86_64/__set_thread_area.s",
+ "musl/src/thread/x86_64/__unmapself.s",
+ "musl/src/thread/x86_64/clone.s",
+ "musl/src/thread/x86_64/syscall_cp.s",
+ "musl/src/time/__map_file.c",
+ "musl/src/time/__month_to_secs.c",
+ "musl/src/time/__secs_to_tm.c",
+ "musl/src/time/__tm_to_secs.c",
+ "musl/src/time/__tz.c",
+ "musl/src/time/__year_to_secs.c",
+ "musl/src/time/asctime.c",
+ "musl/src/time/asctime_r.c",
+ "musl/src/time/clock.c",
+ "musl/src/time/clock_getcpuclockid.c",
+ "musl/src/time/clock_getres.c",
+ "musl/src/time/clock_gettime.c",
+ "musl/src/time/clock_nanosleep.c",
+ "musl/src/time/clock_settime.c",
+ "musl/src/time/ctime.c",
+ "musl/src/time/ctime_r.c",
+ "musl/src/time/difftime.c",
+ "musl/src/time/ftime.c",
+ "musl/src/time/getdate.c",
+ "musl/src/time/gettimeofday.c",
+ "musl/src/time/gmtime.c",
+ "musl/src/time/gmtime_r.c",
+ "musl/src/time/localtime.c",
+ "musl/src/time/localtime_r.c",
+ "musl/src/time/mktime.c",
+ "musl/src/time/nanosleep.c",
+ "musl/src/time/strftime.c",
+ "musl/src/time/strptime.c",
+ "musl/src/time/time.c",
+ "musl/src/time/timegm.c",
+ "musl/src/time/timer_create.c",
+ "musl/src/time/timer_delete.c",
+ "musl/src/time/timer_getoverrun.c",
+ "musl/src/time/timer_gettime.c",
+ "musl/src/time/timer_settime.c",
+ "musl/src/time/times.c",
+ "musl/src/time/timespec_get.c",
+ "musl/src/time/utime.c",
+ "musl/src/time/wcsftime.c",
+ "musl/src/unistd/_exit.c",
+ "musl/src/unistd/access.c",
+ "musl/src/unistd/acct.c",
+ "musl/src/unistd/alarm.c",
+ "musl/src/unistd/chdir.c",
+ "musl/src/unistd/chown.c",
+ "musl/src/unistd/close.c",
+ "musl/src/unistd/ctermid.c",
+ "musl/src/unistd/dup.c",
+ "musl/src/unistd/dup2.c",
+ "musl/src/unistd/dup3.c",
+ "musl/src/unistd/faccessat.c",
+ "musl/src/unistd/fchdir.c",
+ "musl/src/unistd/fchown.c",
+ "musl/src/unistd/fchownat.c",
+ "musl/src/unistd/fdatasync.c",
+ "musl/src/unistd/fsync.c",
+ "musl/src/unistd/ftruncate.c",
+ "musl/src/unistd/getcwd.c",
+ "musl/src/unistd/getegid.c",
+ "musl/src/unistd/geteuid.c",
+ "musl/src/unistd/getgid.c",
+ "musl/src/unistd/getgroups.c",
+ "musl/src/unistd/gethostname.c",
+ "musl/src/unistd/getlogin.c",
+ "musl/src/unistd/getlogin_r.c",
+ "musl/src/unistd/getpgid.c",
+ "musl/src/unistd/getpgrp.c",
+ "musl/src/unistd/getpid.c",
+ "musl/src/unistd/getppid.c",
+ "musl/src/unistd/getsid.c",
+ "musl/src/unistd/getuid.c",
+ "musl/src/unistd/isatty.c",
+ "musl/src/unistd/lchown.c",
+ "musl/src/unistd/link.c",
+ "musl/src/unistd/linkat.c",
+ "musl/src/unistd/lseek.c",
+ "musl/src/unistd/mips/pipe.s",
+ "musl/src/unistd/mips64/pipe.s",
+ "musl/src/unistd/mipsn32/lseek.c",
+ "musl/src/unistd/mipsn32/pipe.s",
+ "musl/src/unistd/nice.c",
+ "musl/src/unistd/pause.c",
+ "musl/src/unistd/pipe.c",
+ "musl/src/unistd/pipe2.c",
+ "musl/src/unistd/posix_close.c",
+ "musl/src/unistd/pread.c",
+ "musl/src/unistd/preadv.c",
+ "musl/src/unistd/pwrite.c",
+ "musl/src/unistd/pwritev.c",
+ "musl/src/unistd/read.c",
+ "musl/src/unistd/readlink.c",
+ "musl/src/unistd/readlinkat.c",
+ "musl/src/unistd/readv.c",
+ "musl/src/unistd/renameat.c",
+ "musl/src/unistd/rmdir.c",
+ "musl/src/unistd/setegid.c",
+ "musl/src/unistd/seteuid.c",
+ "musl/src/unistd/setgid.c",
+ "musl/src/unistd/setpgid.c",
+ "musl/src/unistd/setpgrp.c",
+ "musl/src/unistd/setregid.c",
+ "musl/src/unistd/setresgid.c",
+ "musl/src/unistd/setresuid.c",
+ "musl/src/unistd/setreuid.c",
+ "musl/src/unistd/setsid.c",
+ "musl/src/unistd/setuid.c",
+ "musl/src/unistd/setxid.c",
+ "musl/src/unistd/sh/pipe.s",
+ "musl/src/unistd/sleep.c",
+ "musl/src/unistd/symlink.c",
+ "musl/src/unistd/symlinkat.c",
+ "musl/src/unistd/sync.c",
+ "musl/src/unistd/tcgetpgrp.c",
+ "musl/src/unistd/tcsetpgrp.c",
+ "musl/src/unistd/truncate.c",
+ "musl/src/unistd/ttyname.c",
+ "musl/src/unistd/ttyname_r.c",
+ "musl/src/unistd/ualarm.c",
+ "musl/src/unistd/unlink.c",
+ "musl/src/unistd/unlinkat.c",
+ "musl/src/unistd/usleep.c",
+ "musl/src/unistd/write.c",
+ "musl/src/unistd/writev.c",
+ "musl/src/unistd/x32/lseek.c",
+};
+const compat_time32_files = [_][]const u8{
+ "musl/compat/time32/__xstat.c",
+ "musl/compat/time32/adjtime32.c",
+ "musl/compat/time32/adjtimex_time32.c",
+ "musl/compat/time32/aio_suspend_time32.c",
+ "musl/compat/time32/clock_adjtime32.c",
+ "musl/compat/time32/clock_getres_time32.c",
+ "musl/compat/time32/clock_gettime32.c",
+ "musl/compat/time32/clock_nanosleep_time32.c",
+ "musl/compat/time32/clock_settime32.c",
+ "musl/compat/time32/cnd_timedwait_time32.c",
+ "musl/compat/time32/ctime32.c",
+ "musl/compat/time32/ctime32_r.c",
+ "musl/compat/time32/difftime32.c",
+ "musl/compat/time32/fstat_time32.c",
+ "musl/compat/time32/fstatat_time32.c",
+ "musl/compat/time32/ftime32.c",
+ "musl/compat/time32/futimens_time32.c",
+ "musl/compat/time32/futimes_time32.c",
+ "musl/compat/time32/futimesat_time32.c",
+ "musl/compat/time32/getitimer_time32.c",
+ "musl/compat/time32/getrusage_time32.c",
+ "musl/compat/time32/gettimeofday_time32.c",
+ "musl/compat/time32/gmtime32.c",
+ "musl/compat/time32/gmtime32_r.c",
+ "musl/compat/time32/localtime32.c",
+ "musl/compat/time32/localtime32_r.c",
+ "musl/compat/time32/lstat_time32.c",
+ "musl/compat/time32/lutimes_time32.c",
+ "musl/compat/time32/mktime32.c",
+ "musl/compat/time32/mq_timedreceive_time32.c",
+ "musl/compat/time32/mq_timedsend_time32.c",
+ "musl/compat/time32/mtx_timedlock_time32.c",
+ "musl/compat/time32/nanosleep_time32.c",
+ "musl/compat/time32/ppoll_time32.c",
+ "musl/compat/time32/pselect_time32.c",
+ "musl/compat/time32/pthread_cond_timedwait_time32.c",
+ "musl/compat/time32/pthread_mutex_timedlock_time32.c",
+ "musl/compat/time32/pthread_rwlock_timedrdlock_time32.c",
+ "musl/compat/time32/pthread_rwlock_timedwrlock_time32.c",
+ "musl/compat/time32/pthread_timedjoin_np_time32.c",
+ "musl/compat/time32/recvmmsg_time32.c",
+ "musl/compat/time32/sched_rr_get_interval_time32.c",
+ "musl/compat/time32/select_time32.c",
+ "musl/compat/time32/sem_timedwait_time32.c",
+ "musl/compat/time32/semtimedop_time32.c",
+ "musl/compat/time32/setitimer_time32.c",
+ "musl/compat/time32/settimeofday_time32.c",
+ "musl/compat/time32/sigtimedwait_time32.c",
+ "musl/compat/time32/stat_time32.c",
+ "musl/compat/time32/stime32.c",
+ "musl/compat/time32/thrd_sleep_time32.c",
+ "musl/compat/time32/time32.c",
+ "musl/compat/time32/time32gm.c",
+ "musl/compat/time32/timer_gettime32.c",
+ "musl/compat/time32/timer_settime32.c",
+ "musl/compat/time32/timerfd_gettime32.c",
+ "musl/compat/time32/timerfd_settime32.c",
+ "musl/compat/time32/timespec_get_time32.c",
+ "musl/compat/time32/utime_time32.c",
+ "musl/compat/time32/utimensat_time32.c",
+ "musl/compat/time32/utimes_time32.c",
+ "musl/compat/time32/wait3_time32.c",
+ "musl/compat/time32/wait4_time32.c",
+};
diff --git a/src-self-hosted/print_env.zig b/src/print_env.zig
similarity index 62%
rename from src-self-hosted/print_env.zig
rename to src/print_env.zig
index 9b68633d3e..d1956911e9 100644
--- a/src-self-hosted/print_env.zig
+++ b/src/print_env.zig
@@ -2,39 +2,39 @@ const std = @import("std");
const build_options = @import("build_options");
const introspect = @import("introspect.zig");
const Allocator = std.mem.Allocator;
+const fatal = @import("main.zig").fatal;
pub fn cmdEnv(gpa: *Allocator, args: []const []const u8, stdout: anytype) !void {
- const zig_lib_dir = introspect.resolveZigLibDir(gpa) catch |err| {
- std.debug.print("unable to find zig installation directory: {}\n", .{@errorName(err)});
- std.process.exit(1);
- };
- defer gpa.free(zig_lib_dir);
+ const self_exe_path = try std.fs.selfExePathAlloc(gpa);
+ defer gpa.free(self_exe_path);
- const zig_std_dir = try std.fs.path.join(gpa, &[_][]const u8{ zig_lib_dir, "std" });
+ var zig_lib_directory = introspect.findZigLibDirFromSelfExe(gpa, self_exe_path) catch |err| {
+ fatal("unable to find zig installation directory: {}\n", .{@errorName(err)});
+ };
+ defer gpa.free(zig_lib_directory.path.?);
+ defer zig_lib_directory.handle.close();
+
+ const zig_std_dir = try std.fs.path.join(gpa, &[_][]const u8{ zig_lib_directory.path.?, "std" });
defer gpa.free(zig_std_dir);
const global_cache_dir = try introspect.resolveGlobalCacheDir(gpa);
defer gpa.free(global_cache_dir);
- const compiler_id_digest = try introspect.resolveCompilerId(gpa);
- var compiler_id_buf: [compiler_id_digest.len * 2]u8 = undefined;
- const compiler_id = std.fmt.bufPrint(&compiler_id_buf, "{x}", .{compiler_id_digest}) catch unreachable;
-
var bos = std.io.bufferedOutStream(stdout);
const bos_stream = bos.outStream();
var jws = std.json.WriteStream(@TypeOf(bos_stream), 6).init(bos_stream);
try jws.beginObject();
+ try jws.objectField("zig_exe");
+ try jws.emitString(self_exe_path);
+
try jws.objectField("lib_dir");
- try jws.emitString(zig_lib_dir);
+ try jws.emitString(zig_lib_directory.path.?);
try jws.objectField("std_dir");
try jws.emitString(zig_std_dir);
- try jws.objectField("id");
- try jws.emitString(compiler_id);
-
try jws.objectField("global_cache_dir");
try jws.emitString(global_cache_dir);
diff --git a/src-self-hosted/print_targets.zig b/src/print_targets.zig
similarity index 60%
rename from src-self-hosted/print_targets.zig
rename to src/print_targets.zig
index 0fe755ffb4..724cb7a9ac 100644
--- a/src-self-hosted/print_targets.zig
+++ b/src/print_targets.zig
@@ -4,59 +4,11 @@ const io = std.io;
const mem = std.mem;
const Allocator = mem.Allocator;
const Target = std.Target;
+const target = @import("target.zig");
const assert = std.debug.assert;
-
+const glibc = @import("glibc.zig");
const introspect = @import("introspect.zig");
-
-// TODO this is hard-coded until self-hosted gains this information canonically
-const available_libcs = [_][]const u8{
- "aarch64_be-linux-gnu",
- "aarch64_be-linux-musl",
- "aarch64_be-windows-gnu",
- "aarch64-linux-gnu",
- "aarch64-linux-musl",
- "aarch64-windows-gnu",
- "armeb-linux-gnueabi",
- "armeb-linux-gnueabihf",
- "armeb-linux-musleabi",
- "armeb-linux-musleabihf",
- "armeb-windows-gnu",
- "arm-linux-gnueabi",
- "arm-linux-gnueabihf",
- "arm-linux-musleabi",
- "arm-linux-musleabihf",
- "arm-windows-gnu",
- "i386-linux-gnu",
- "i386-linux-musl",
- "i386-windows-gnu",
- "mips64el-linux-gnuabi64",
- "mips64el-linux-gnuabin32",
- "mips64el-linux-musl",
- "mips64-linux-gnuabi64",
- "mips64-linux-gnuabin32",
- "mips64-linux-musl",
- "mipsel-linux-gnu",
- "mipsel-linux-musl",
- "mips-linux-gnu",
- "mips-linux-musl",
- "powerpc64le-linux-gnu",
- "powerpc64le-linux-musl",
- "powerpc64-linux-gnu",
- "powerpc64-linux-musl",
- "powerpc-linux-gnu",
- "powerpc-linux-musl",
- "riscv64-linux-gnu",
- "riscv64-linux-musl",
- "s390x-linux-gnu",
- "s390x-linux-musl",
- "sparc-linux-gnu",
- "sparcv9-linux-gnu",
- "wasm32-freestanding-musl",
- "x86_64-linux-gnu",
- "x86_64-linux-gnux32",
- "x86_64-linux-musl",
- "x86_64-windows-gnu",
-};
+const fatal = @import("main.zig").fatal;
pub fn cmdTargets(
allocator: *Allocator,
@@ -65,33 +17,14 @@ pub fn cmdTargets(
stdout: anytype,
native_target: Target,
) !void {
- const available_glibcs = blk: {
- const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch |err| {
- std.debug.print("unable to find zig installation directory: {}\n", .{@errorName(err)});
- std.process.exit(1);
- };
- defer allocator.free(zig_lib_dir);
-
- var dir = try std.fs.cwd().openDir(zig_lib_dir, .{});
- defer dir.close();
-
- const vers_txt = try dir.readFileAlloc(allocator, "libc" ++ std.fs.path.sep_str ++ "glibc" ++ std.fs.path.sep_str ++ "vers.txt", 10 * 1024);
- defer allocator.free(vers_txt);
-
- var list = std.ArrayList(std.builtin.Version).init(allocator);
- defer list.deinit();
-
- var it = mem.tokenize(vers_txt, "\r\n");
- while (it.next()) |line| {
- const prefix = "GLIBC_";
- assert(mem.startsWith(u8, line, prefix));
- const adjusted_line = line[prefix.len..];
- const ver = try std.builtin.Version.parse(adjusted_line);
- try list.append(ver);
- }
- break :blk list.toOwnedSlice();
+ var zig_lib_directory = introspect.findZigLibDir(allocator) catch |err| {
+ fatal("unable to find zig installation directory: {}\n", .{@errorName(err)});
};
- defer allocator.free(available_glibcs);
+ defer zig_lib_directory.handle.close();
+ defer allocator.free(zig_lib_directory.path.?);
+
+ const glibc_abi = try glibc.loadMetaData(allocator, zig_lib_directory.handle);
+ defer glibc_abi.destroy(allocator);
var bos = io.bufferedOutStream(stdout);
const bos_stream = bos.outStream();
@@ -127,18 +60,22 @@ pub fn cmdTargets(
try jws.objectField("libc");
try jws.beginArray();
- for (available_libcs) |libc| {
+ for (target.available_libcs) |libc| {
+ const tmp = try std.fmt.allocPrint(allocator, "{}-{}-{}", .{
+ @tagName(libc.arch), @tagName(libc.os), @tagName(libc.abi),
+ });
+ defer allocator.free(tmp);
try jws.arrayElem();
- try jws.emitString(libc);
+ try jws.emitString(tmp);
}
try jws.endArray();
try jws.objectField("glibc");
try jws.beginArray();
- for (available_glibcs) |glibc| {
+ for (glibc_abi.all_versions) |ver| {
try jws.arrayElem();
- const tmp = try std.fmt.allocPrint(allocator, "{}", .{glibc});
+ const tmp = try std.fmt.allocPrint(allocator, "{}", .{ver});
defer allocator.free(tmp);
try jws.emitString(tmp);
}
@@ -215,7 +152,6 @@ pub fn cmdTargets(
try jws.emitString(@tagName(native_target.os.tag));
try jws.objectField("abi");
try jws.emitString(@tagName(native_target.abi));
- // TODO implement native glibc version detection in self-hosted
try jws.endObject();
try jws.endObject();
diff --git a/src/stage1.zig b/src/stage1.zig
new file mode 100644
index 0000000000..a989ad4be3
--- /dev/null
+++ b/src/stage1.zig
@@ -0,0 +1,426 @@
+//! This is the main entry point for the Zig/C++ hybrid compiler (stage1).
+//! It has the functions exported from Zig, called in C++, and bindings for
+//! the functions exported from C++, called from Zig.
+
+const std = @import("std");
+const assert = std.debug.assert;
+const mem = std.mem;
+const CrossTarget = std.zig.CrossTarget;
+const Target = std.Target;
+
+const build_options = @import("build_options");
+const stage2 = @import("main.zig");
+const fatal = stage2.fatal;
+const Compilation = @import("Compilation.zig");
+const translate_c = @import("translate_c.zig");
+const target_util = @import("target.zig");
+
+comptime {
+ assert(std.builtin.link_libc);
+ assert(build_options.is_stage1);
+ assert(build_options.have_llvm);
+ _ = @import("compiler_rt");
+}
+
+pub const log = stage2.log;
+pub const log_level = stage2.log_level;
+
+pub export fn main(argc: c_int, argv: [*]const [*:0]const u8) c_int {
+ std.debug.maybeEnableSegfaultHandler();
+
+ zig_stage1_os_init();
+
+ const gpa = std.heap.c_allocator;
+ var arena_instance = std.heap.ArenaAllocator.init(gpa);
+ defer arena_instance.deinit();
+ const arena = &arena_instance.allocator;
+
+ const args = arena.alloc([]const u8, @intCast(usize, argc)) catch fatal("{}", .{"OutOfMemory"});
+ for (args) |*arg, i| {
+ arg.* = mem.spanZ(argv[i]);
+ }
+ stage2.mainArgs(gpa, arena, args) catch |err| fatal("{}", .{@errorName(err)});
+ return 0;
+}
+
+/// Matches stage2.Color;
+pub const ErrColor = c_int;
+/// Matches std.builtin.CodeModel
+pub const CodeModel = c_int;
+/// Matches std.Target.Os.Tag
+pub const OS = c_int;
+/// Matches std.builtin.BuildMode
+pub const BuildMode = c_int;
+
+pub const TargetSubsystem = extern enum(c_int) {
+ Console,
+ Windows,
+ Posix,
+ Native,
+ EfiApplication,
+ EfiBootServiceDriver,
+ EfiRom,
+ EfiRuntimeDriver,
+ Auto,
+};
+
+pub const Pkg = extern struct {
+ name_ptr: [*]const u8,
+ name_len: usize,
+ path_ptr: [*]const u8,
+ path_len: usize,
+ children_ptr: [*]*Pkg,
+ children_len: usize,
+ parent: ?*Pkg,
+};
+
+pub const Module = extern struct {
+ root_name_ptr: [*]const u8,
+ root_name_len: usize,
+ emit_o_ptr: [*]const u8,
+ emit_o_len: usize,
+ emit_h_ptr: [*]const u8,
+ emit_h_len: usize,
+ emit_asm_ptr: [*]const u8,
+ emit_asm_len: usize,
+ emit_llvm_ir_ptr: [*]const u8,
+ emit_llvm_ir_len: usize,
+ emit_analysis_json_ptr: [*]const u8,
+ emit_analysis_json_len: usize,
+ emit_docs_ptr: [*]const u8,
+ emit_docs_len: usize,
+ builtin_zig_path_ptr: [*]const u8,
+ builtin_zig_path_len: usize,
+ test_filter_ptr: [*]const u8,
+ test_filter_len: usize,
+ test_name_prefix_ptr: [*]const u8,
+ test_name_prefix_len: usize,
+ userdata: usize,
+ root_pkg: *Pkg,
+ main_progress_node: ?*std.Progress.Node,
+ code_model: CodeModel,
+ subsystem: TargetSubsystem,
+ err_color: ErrColor,
+ pic: bool,
+ link_libc: bool,
+ link_libcpp: bool,
+ strip: bool,
+ is_single_threaded: bool,
+ dll_export_fns: bool,
+ link_mode_dynamic: bool,
+ valgrind_enabled: bool,
+ function_sections: bool,
+ enable_stack_probing: bool,
+ enable_time_report: bool,
+ enable_stack_report: bool,
+ test_is_evented: bool,
+ verbose_tokenize: bool,
+ verbose_ast: bool,
+ verbose_ir: bool,
+ verbose_llvm_ir: bool,
+ verbose_cimport: bool,
+ verbose_llvm_cpu_features: bool,
+
+ // Set by stage1
+ have_c_main: bool,
+ have_winmain: bool,
+ have_wwinmain: bool,
+ have_winmain_crt_startup: bool,
+ have_wwinmain_crt_startup: bool,
+ have_dllmain_crt_startup: bool,
+
+ pub fn build_object(mod: *Module) void {
+ zig_stage1_build_object(mod);
+ }
+
+ pub fn destroy(mod: *Module) void {
+ zig_stage1_destroy(mod);
+ }
+};
+
+extern fn zig_stage1_os_init() void;
+
+pub const create = zig_stage1_create;
+extern fn zig_stage1_create(
+ optimize_mode: BuildMode,
+ main_pkg_path_ptr: [*]const u8,
+ main_pkg_path_len: usize,
+ root_src_path_ptr: [*]const u8,
+ root_src_path_len: usize,
+ zig_lib_dir_ptr: [*c]const u8,
+ zig_lib_dir_len: usize,
+ target: [*c]const Stage2Target,
+ is_test_build: bool,
+) ?*Module;
+
+extern fn zig_stage1_build_object(*Module) void;
+extern fn zig_stage1_destroy(*Module) void;
+
+// ABI warning
+export fn stage2_panic(ptr: [*]const u8, len: usize) void {
+ @panic(ptr[0..len]);
+}
+
+// ABI warning
+const Error = extern enum {
+ None,
+ OutOfMemory,
+ InvalidFormat,
+ SemanticAnalyzeFail,
+ AccessDenied,
+ Interrupted,
+ SystemResources,
+ FileNotFound,
+ FileSystem,
+ FileTooBig,
+ DivByZero,
+ Overflow,
+ PathAlreadyExists,
+ Unexpected,
+ ExactDivRemainder,
+ NegativeDenominator,
+ ShiftedOutOneBits,
+ CCompileErrors,
+ EndOfFile,
+ IsDir,
+ NotDir,
+ UnsupportedOperatingSystem,
+ SharingViolation,
+ PipeBusy,
+ PrimitiveTypeNotFound,
+ CacheUnavailable,
+ PathTooLong,
+ CCompilerCannotFindFile,
+ NoCCompilerInstalled,
+ ReadingDepFile,
+ InvalidDepFile,
+ MissingArchitecture,
+ MissingOperatingSystem,
+ UnknownArchitecture,
+ UnknownOperatingSystem,
+ UnknownABI,
+ InvalidFilename,
+ DiskQuota,
+ DiskSpace,
+ UnexpectedWriteFailure,
+ UnexpectedSeekFailure,
+ UnexpectedFileTruncationFailure,
+ Unimplemented,
+ OperationAborted,
+ BrokenPipe,
+ NoSpaceLeft,
+ NotLazy,
+ IsAsync,
+ ImportOutsidePkgPath,
+ UnknownCpuModel,
+ UnknownCpuFeature,
+ InvalidCpuFeatures,
+ InvalidLlvmCpuFeaturesFormat,
+ UnknownApplicationBinaryInterface,
+ ASTUnitFailure,
+ BadPathName,
+ SymLinkLoop,
+ ProcessFdQuotaExceeded,
+ SystemFdQuotaExceeded,
+ NoDevice,
+ DeviceBusy,
+ UnableToSpawnCCompiler,
+ CCompilerExitCode,
+ CCompilerCrashed,
+ CCompilerCannotFindHeaders,
+ LibCRuntimeNotFound,
+ LibCStdLibHeaderNotFound,
+ LibCKernel32LibNotFound,
+ UnsupportedArchitecture,
+ WindowsSdkNotFound,
+ UnknownDynamicLinkerPath,
+ TargetHasNoDynamicLinker,
+ InvalidAbiVersion,
+ InvalidOperatingSystemVersion,
+ UnknownClangOption,
+ NestedResponseFile,
+ ZigIsTheCCompiler,
+ FileBusy,
+ Locked,
+};
+
+// ABI warning
+export fn stage2_attach_segfault_handler() void {
+ if (std.debug.runtime_safety and std.debug.have_segfault_handling_support) {
+ std.debug.attachSegfaultHandler();
+ }
+}
+
+// ABI warning
+export fn stage2_progress_create() *std.Progress {
+ const ptr = std.heap.c_allocator.create(std.Progress) catch @panic("out of memory");
+ ptr.* = std.Progress{};
+ return ptr;
+}
+
+// ABI warning
+export fn stage2_progress_destroy(progress: *std.Progress) void {
+ std.heap.c_allocator.destroy(progress);
+}
+
+// ABI warning
+export fn stage2_progress_start_root(
+ progress: *std.Progress,
+ name_ptr: [*]const u8,
+ name_len: usize,
+ estimated_total_items: usize,
+) *std.Progress.Node {
+ return progress.start(
+ name_ptr[0..name_len],
+ if (estimated_total_items == 0) null else estimated_total_items,
+ ) catch @panic("timer unsupported");
+}
+
+// ABI warning
+export fn stage2_progress_disable_tty(progress: *std.Progress) void {
+ progress.terminal = null;
+}
+
+// ABI warning
+export fn stage2_progress_start(
+ node: *std.Progress.Node,
+ name_ptr: [*]const u8,
+ name_len: usize,
+ estimated_total_items: usize,
+) *std.Progress.Node {
+ const child_node = std.heap.c_allocator.create(std.Progress.Node) catch @panic("out of memory");
+ child_node.* = node.start(
+ name_ptr[0..name_len],
+ if (estimated_total_items == 0) null else estimated_total_items,
+ );
+ child_node.activate();
+ return child_node;
+}
+
+// ABI warning
+export fn stage2_progress_end(node: *std.Progress.Node) void {
+ node.end();
+ if (&node.context.root != node) {
+ std.heap.c_allocator.destroy(node);
+ }
+}
+
+// ABI warning
+export fn stage2_progress_complete_one(node: *std.Progress.Node) void {
+ node.completeOne();
+}
+
+// ABI warning
+export fn stage2_progress_update_node(node: *std.Progress.Node, done_count: usize, total_count: usize) void {
+ node.completed_items = done_count;
+ node.estimated_total_items = total_count;
+ node.activate();
+ node.context.maybeRefresh();
+}
+
+// ABI warning
+pub const Stage2Target = extern struct {
+ arch: c_int,
+ os: OS,
+ abi: c_int,
+
+ is_native_os: bool,
+ is_native_cpu: bool,
+
+ llvm_cpu_name: ?[*:0]const u8,
+ llvm_cpu_features: ?[*:0]const u8,
+};
+
+// ABI warning
+const Stage2SemVer = extern struct {
+ major: u32,
+ minor: u32,
+ patch: u32,
+};
+
+// ABI warning
+export fn stage2_cimport(
+ stage1: *Module,
+ c_src_ptr: [*]const u8,
+ c_src_len: usize,
+ out_zig_path_ptr: *[*]const u8,
+ out_zig_path_len: *usize,
+ out_errors_ptr: *[*]translate_c.ClangErrMsg,
+ out_errors_len: *usize,
+) Error {
+ const comp = @intToPtr(*Compilation, stage1.userdata);
+ const c_src = c_src_ptr[0..c_src_len];
+ const result = comp.cImport(c_src) catch |err| switch (err) {
+ error.SystemResources => return .SystemResources,
+ error.OperationAborted => return .OperationAborted,
+ error.BrokenPipe => return .BrokenPipe,
+ error.DiskQuota => return .DiskQuota,
+ error.FileTooBig => return .FileTooBig,
+ error.NoSpaceLeft => return .NoSpaceLeft,
+ error.AccessDenied => return .AccessDenied,
+ error.OutOfMemory => return .OutOfMemory,
+ error.Unexpected => return .Unexpected,
+ error.InputOutput => return .FileSystem,
+ error.ASTUnitFailure => return .ASTUnitFailure,
+ error.CacheUnavailable => return .CacheUnavailable,
+ else => return .Unexpected,
+ };
+ out_zig_path_ptr.* = result.out_zig_path.ptr;
+ out_zig_path_len.* = result.out_zig_path.len;
+ out_errors_ptr.* = result.errors.ptr;
+ out_errors_len.* = result.errors.len;
+ if (result.errors.len != 0) return .CCompileErrors;
+ return Error.None;
+}
+
+export fn stage2_add_link_lib(
+ stage1: *Module,
+ lib_name_ptr: [*c]const u8,
+ lib_name_len: usize,
+ symbol_name_ptr: [*c]const u8,
+ symbol_name_len: usize,
+) ?[*:0]const u8 {
+ const comp = @intToPtr(*Compilation, stage1.userdata);
+ const lib_name = std.ascii.allocLowerString(comp.gpa, lib_name_ptr[0..lib_name_len]) catch return "out of memory";
+ const target = comp.getTarget();
+ const is_libc = target_util.is_libc_lib_name(target, lib_name);
+ if (is_libc) {
+ if (!comp.bin_file.options.link_libc) {
+ return "dependency on libc must be explicitly specified in the build command";
+ }
+ return null;
+ }
+ if (target_util.is_libcpp_lib_name(target, lib_name)) {
+ if (!comp.bin_file.options.link_libcpp) {
+ return "dependency on libc++ must be explicitly specified in the build command";
+ }
+ return null;
+ }
+ if (!target.isWasm() and !comp.bin_file.options.pic) {
+ return std.fmt.allocPrint0(
+ comp.gpa,
+ "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.",
+ .{ lib_name, lib_name },
+ ) catch "out of memory";
+ }
+ comp.stage1AddLinkLib(lib_name) catch |err| {
+ return std.fmt.allocPrint0(comp.gpa, "unable to add link lib '{s}': {s}", .{
+ lib_name, @errorName(err),
+ }) catch "out of memory";
+ };
+ return null;
+}
+
+export fn stage2_fetch_file(
+ stage1: *Module,
+ path_ptr: [*]const u8,
+ path_len: usize,
+ result_len: *usize,
+) ?[*]const u8 {
+ const comp = @intToPtr(*Compilation, stage1.userdata);
+ const file_path = path_ptr[0..path_len];
+ const max_file_size = std.math.maxInt(u32);
+ const contents = comp.stage1_cache_manifest.addFilePostFetch(file_path, max_file_size) catch return null;
+ result_len.* = contents.len;
+ return contents.ptr;
+}
diff --git a/src/all_types.hpp b/src/stage1/all_types.hpp
similarity index 96%
rename from src/all_types.hpp
rename to src/stage1/all_types.hpp
index 1fa04f2b79..7a5016d004 100644
--- a/src/all_types.hpp
+++ b/src/stage1/all_types.hpp
@@ -10,7 +10,6 @@
#include "list.hpp"
#include "buffer.hpp"
-#include "cache_hash.hpp"
#include "zig_llvm.h"
#include "hash_map.hpp"
#include "errmsg.hpp"
@@ -1639,8 +1638,6 @@ struct ZigType {
size_t abi_size;
// Number of bits of information in this type. Known after ResolveStatusSizeKnown.
size_t size_in_bits;
-
- bool gen_h_loop_flag;
};
enum FnAnalState {
@@ -1976,67 +1973,16 @@ struct TimeEvent {
const char *name;
};
-enum BuildMode {
- BuildModeDebug,
- BuildModeFastRelease,
- BuildModeSafeRelease,
- BuildModeSmallRelease,
-};
-
-enum CodeModel {
- CodeModelDefault,
- CodeModelTiny,
- CodeModelSmall,
- CodeModelKernel,
- CodeModelMedium,
- CodeModelLarge,
-};
-
-struct LinkLib {
- Buf *name;
- Buf *path;
- ZigList symbols; // the list of symbols that we depend on from this lib
- bool provided_explicitly;
-};
-
-enum ValgrindSupport {
- ValgrindSupportAuto,
- ValgrindSupportDisabled,
- ValgrindSupportEnabled,
-};
-
-enum WantPIC {
- WantPICAuto,
- WantPICDisabled,
- WantPICEnabled,
-};
-
-enum WantStackCheck {
- WantStackCheckAuto,
- WantStackCheckDisabled,
- WantStackCheckEnabled,
-};
-
-enum WantCSanitize {
- WantCSanitizeAuto,
- WantCSanitizeDisabled,
- WantCSanitizeEnabled,
-};
-
-enum OptionalBool {
- OptionalBoolNull,
- OptionalBoolFalse,
- OptionalBoolTrue,
-};
-
struct CFile {
ZigList args;
const char *source_path;
const char *preprocessor_only_basename;
};
-// When adding fields, check if they should be added to the hash computation in build_with_cache
struct CodeGen {
+ // Other code depends on this being first.
+ ZigStage1 stage1;
+
// arena allocator destroyed just prior to codegen emit
heap::ArenaAllocator *pass1_arena;
@@ -2048,8 +1994,6 @@ struct CodeGen {
ZigLLVMDIBuilder *dbuilder;
ZigLLVMDICompileUnit *compile_unit;
ZigLLVMDIFile *compile_unit_file;
- LinkLib *libc_link_lib;
- LinkLib *libcpp_link_lib;
LLVMTargetDataRef target_data_ref;
LLVMTargetMachineRef target_machine;
ZigLLVMDIFile *dummy_di_file;
@@ -2104,7 +2048,6 @@ struct CodeGen {
ZigList inline_fns;
ZigList test_fns;
ZigList errors_by_index;
- ZigList caches_to_release;
size_t largest_err_name_len;
ZigList type_resolve_stack;
@@ -2173,18 +2116,17 @@ struct CodeGen {
Buf llvm_triple_str;
Buf global_asm;
Buf o_file_output_path;
- Buf bin_file_output_path;
+ Buf h_file_output_path;
Buf asm_file_output_path;
Buf llvm_ir_file_output_path;
+ Buf analysis_json_output_path;
+ Buf docs_output_path;
Buf *cache_dir;
- // As an input parameter, mutually exclusive with enable_cache. But it gets
- // populated in codegen_build_and_link.
- Buf *output_dir;
Buf *c_artifact_dir;
const char **libc_include_dir_list;
size_t libc_include_dir_len;
- Buf *zig_c_headers_dir; // Cannot be overridden; derived from zig_lib_dir.
+ Buf *builtin_zig_path;
Buf *zig_std_special_dir; // Cannot be overridden; derived from zig_lib_dir.
IrInstSrc *invalid_inst_src;
@@ -2206,118 +2148,46 @@ struct CodeGen {
Stage2ProgressNode *main_progress_node;
Stage2ProgressNode *sub_progress_node;
- WantPIC want_pic;
- WantStackCheck want_stack_check;
- WantCSanitize want_sanitize_c;
- CacheHash cache_hash;
ErrColor err_color;
uint32_t next_unresolved_index;
unsigned pointer_size_bytes;
- uint32_t target_os_index;
- uint32_t target_arch_index;
- uint32_t target_sub_arch_index;
- uint32_t target_abi_index;
- uint32_t target_oformat_index;
bool is_big_endian;
- bool have_c_main;
- bool have_winmain;
- bool have_wwinmain;
- bool have_winmain_crt_startup;
- bool have_wwinmain_crt_startup;
- bool have_dllmain_crt_startup;
bool have_err_ret_tracing;
- bool link_eh_frame_hdr;
- bool c_want_stdint;
- bool c_want_stdbool;
bool verbose_tokenize;
bool verbose_ast;
- bool verbose_link;
bool verbose_ir;
bool verbose_llvm_ir;
bool verbose_cimport;
- bool verbose_cc;
bool verbose_llvm_cpu_features;
bool error_during_imports;
bool generate_error_name_table;
- bool enable_cache; // mutually exclusive with output_dir
bool enable_time_report;
bool enable_stack_report;
- bool system_linker_hack;
bool reported_bad_link_libc_error;
- bool is_dynamic; // shared library rather than static library. dynamic musl rather than static musl.
bool need_frame_size_prefix_data;
- bool disable_c_depfile;
-
- //////////////////////////// Participates in Input Parameter Cache Hash
- /////// Note: there is a separate cache hash for builtin.zig, when adding fields,
- /////// consider if they need to go into both.
- ZigList link_libs_list;
- // add -framework [name] args to linker
- ZigList darwin_frameworks;
- // add -rpath [name] args to linker
- ZigList rpath_list;
- ZigList forbidden_libs;
- ZigList link_objects;
- ZigList assembly_files;
- ZigList c_source_files;
- ZigList lib_dirs;
- ZigList framework_dirs;
-
- Stage2LibCInstallation *libc;
-
- bool is_versioned;
- size_t version_major;
- size_t version_minor;
- size_t version_patch;
- const char *linker_script;
- size_t stack_size_override;
+ bool link_libc;
+ bool link_libcpp;
BuildMode build_mode;
- OutType out_type;
const ZigTarget *zig_target;
TargetSubsystem subsystem; // careful using this directly; see detect_subsystem
- ValgrindSupport valgrind_support;
CodeModel code_model;
- OptionalBool linker_gc_sections;
- OptionalBool linker_allow_shlib_undefined;
- OptionalBool linker_bind_global_refs_locally;
bool strip_debug_symbols;
bool is_test_build;
bool is_single_threaded;
- bool want_single_threaded;
- bool linker_rdynamic;
- bool each_lib_rpath;
- bool is_dummy_so;
- bool disable_gen_h;
- bool bundle_compiler_rt;
bool have_pic;
- bool have_dynamic_link; // this is whether the final thing will be dynamically linked. see also is_dynamic
+ bool link_mode_dynamic;
+ bool dll_export_fns;
bool have_stack_probing;
- bool have_sanitize_c;
bool function_sections;
- bool enable_dump_analysis;
- bool enable_doc_generation;
- bool emit_bin;
- bool emit_asm;
- bool emit_llvm_ir;
bool test_is_evented;
- bool linker_z_nodelete;
- bool linker_z_defs;
+ bool valgrind_enabled;
Buf *root_out_name;
Buf *test_filter;
Buf *test_name_prefix;
Buf *zig_lib_dir;
Buf *zig_std_dir;
- Buf *version_script_path;
- Buf *override_soname;
- Buf *linker_optimization;
-
- const char **llvm_argv;
- size_t llvm_argv_len;
-
- const char **clang_argv;
- size_t clang_argv_len;
};
struct ZigVar {
diff --git a/src/analyze.cpp b/src/stage1/analyze.cpp
similarity index 99%
rename from src/analyze.cpp
rename to src/stage1/analyze.cpp
index 3ba4fd7928..369c284684 100644
--- a/src/analyze.cpp
+++ b/src/stage1/analyze.cpp
@@ -825,6 +825,7 @@ ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, Zi
}
ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {
+ Error err;
assert(ptr_type->id == ZigTypeIdPointer);
assert(ptr_type->data.pointer.ptr_len == PtrLenUnknown);
@@ -833,6 +834,11 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {
return *parent_pointer;
}
+ // We use the pointer type's abi size below, so we have to resolve it now.
+ if ((err = type_resolve(g, ptr_type, ResolveStatusSizeKnown))) {
+ codegen_report_errors_and_exit(g);
+ }
+
ZigType *entry = new_type_table_entry(ZigTypeIdStruct);
buf_resize(&entry->name, 0);
@@ -3489,19 +3495,19 @@ void add_var_export(CodeGen *g, ZigVar *var, const char *symbol_name, GlobalLink
}
void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, CallingConvention cc) {
- if (cc == CallingConventionC && strcmp(symbol_name, "main") == 0 && g->libc_link_lib != nullptr) {
- g->have_c_main = true;
+ if (cc == CallingConventionC && strcmp(symbol_name, "main") == 0 && g->link_libc) {
+ g->stage1.have_c_main = true;
} else if (cc == CallingConventionStdcall && g->zig_target->os == OsWindows) {
if (strcmp(symbol_name, "WinMain") == 0) {
- g->have_winmain = true;
+ g->stage1.have_winmain = true;
} else if (strcmp(symbol_name, "wWinMain") == 0) {
- g->have_wwinmain = true;
+ g->stage1.have_wwinmain = true;
} else if (strcmp(symbol_name, "WinMainCRTStartup") == 0) {
- g->have_winmain_crt_startup = true;
+ g->stage1.have_winmain_crt_startup = true;
} else if (strcmp(symbol_name, "wWinMainCRTStartup") == 0) {
- g->have_wwinmain_crt_startup = true;
+ g->stage1.have_wwinmain_crt_startup = true;
} else if (strcmp(symbol_name, "DllMainCRTStartup") == 0) {
- g->have_dllmain_crt_startup = true;
+ g->stage1.have_dllmain_crt_startup = true;
}
}
@@ -7856,40 +7862,6 @@ const char *type_id_name(ZigTypeId id) {
zig_unreachable();
}
-LinkLib *create_link_lib(Buf *name) {
- LinkLib *link_lib = heap::c_allocator.create();
- link_lib->name = name;
- return link_lib;
-}
-
-LinkLib *add_link_lib(CodeGen *g, Buf *name) {
- bool is_libc = buf_eql_str(name, "c");
- bool is_libcpp = buf_eql_str(name, "c++") || buf_eql_str(name, "c++abi");
-
- if (is_libc && g->libc_link_lib != nullptr)
- return g->libc_link_lib;
-
- if (is_libcpp && g->libcpp_link_lib != nullptr)
- return g->libcpp_link_lib;
-
- for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
- LinkLib *existing_lib = g->link_libs_list.at(i);
- if (buf_eql_buf(existing_lib->name, name)) {
- return existing_lib;
- }
- }
-
- LinkLib *link_lib = create_link_lib(name);
- g->link_libs_list.append(link_lib);
-
- if (is_libc)
- g->libc_link_lib = link_lib;
- if (is_libcpp)
- g->libcpp_link_lib = link_lib;
-
- return link_lib;
-}
-
ZigType *get_align_amt_type(CodeGen *g) {
if (g->align_amt_type == nullptr) {
// according to LLVM the maximum alignment is 1 << 29.
@@ -8010,12 +7982,13 @@ not_integer:
return ErrorNone;
}
-Error file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents) {
- if (g->enable_cache) {
- return cache_add_file_fetch(&g->cache_hash, resolved_path, contents);
- } else {
- return os_fetch_file_path(resolved_path, contents);
- }
+Error file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents_buf) {
+ size_t len;
+ const char *contents = stage2_fetch_file(&g->stage1, buf_ptr(resolved_path), buf_len(resolved_path), &len);
+ if (contents == nullptr)
+ return ErrorFileNotFound;
+ buf_init_from_mem(contents_buf, contents, len);
+ return ErrorNone;
}
static X64CABIClass type_windows_abi_x86_64_class(CodeGen *g, ZigType *ty, size_t ty_size) {
diff --git a/src/analyze.hpp b/src/stage1/analyze.hpp
similarity index 98%
rename from src/analyze.hpp
rename to src/stage1/analyze.hpp
index 0df1a4ba91..07601e6dea 100644
--- a/src/analyze.hpp
+++ b/src/stage1/analyze.hpp
@@ -200,8 +200,6 @@ ZigTypeId type_id_at_index(size_t index);
size_t type_id_len();
size_t type_id_index(ZigType *entry);
ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id);
-LinkLib *create_link_lib(Buf *name);
-LinkLib *add_link_lib(CodeGen *codegen, Buf *lib);
bool optional_value_is_null(ZigValue *val);
uint32_t get_abi_alignment(CodeGen *g, ZigType *type_entry);
@@ -256,7 +254,6 @@ Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_no
void typecheck_panic_fn(CodeGen *g, TldFn *tld_fn, ZigFn *panic_fn);
Buf *type_bare_name(ZigType *t);
Buf *type_h_name(ZigType *t);
-Error create_c_object_cache(CodeGen *g, CacheHash **out_cache_hash, bool verbose);
LLVMTypeRef get_llvm_type(CodeGen *g, ZigType *type);
ZigLLVMDIType *get_llvm_di_type(CodeGen *g, ZigType *type);
diff --git a/src/ast_render.cpp b/src/stage1/ast_render.cpp
similarity index 100%
rename from src/ast_render.cpp
rename to src/stage1/ast_render.cpp
diff --git a/src/ast_render.hpp b/src/stage1/ast_render.hpp
similarity index 100%
rename from src/ast_render.hpp
rename to src/stage1/ast_render.hpp
diff --git a/src/bigfloat.cpp b/src/stage1/bigfloat.cpp
similarity index 100%
rename from src/bigfloat.cpp
rename to src/stage1/bigfloat.cpp
diff --git a/src/bigfloat.hpp b/src/stage1/bigfloat.hpp
similarity index 100%
rename from src/bigfloat.hpp
rename to src/stage1/bigfloat.hpp
diff --git a/src/bigint.cpp b/src/stage1/bigint.cpp
similarity index 100%
rename from src/bigint.cpp
rename to src/stage1/bigint.cpp
diff --git a/src/bigint.hpp b/src/stage1/bigint.hpp
similarity index 100%
rename from src/bigint.hpp
rename to src/stage1/bigint.hpp
diff --git a/src/buffer.cpp b/src/stage1/buffer.cpp
similarity index 100%
rename from src/buffer.cpp
rename to src/stage1/buffer.cpp
diff --git a/src/buffer.hpp b/src/stage1/buffer.hpp
similarity index 100%
rename from src/buffer.hpp
rename to src/stage1/buffer.hpp
diff --git a/src/codegen.cpp b/src/stage1/codegen.cpp
similarity index 84%
rename from src/codegen.cpp
rename to src/stage1/codegen.cpp
index 2a216f69c8..8513f40d24 100644
--- a/src/codegen.cpp
+++ b/src/stage1/codegen.cpp
@@ -8,7 +8,6 @@
#include "analyze.hpp"
#include "ast_render.hpp"
#include "codegen.hpp"
-#include "compiler.hpp"
#include "config.h"
#include "errmsg.hpp"
#include "error.hpp"
@@ -21,7 +20,6 @@
#include "stage2.h"
#include "dump_analysis.hpp"
#include "softfloat.hpp"
-#include "mem_profile.hpp"
#include
#include
@@ -73,39 +71,6 @@ static const char *symbols_that_llvm_depends_on[] = {
// TODO probably all of compiler-rt needs to go here
};
-void codegen_set_clang_argv(CodeGen *g, const char **args, size_t len) {
- g->clang_argv = args;
- g->clang_argv_len = len;
-}
-
-void codegen_set_llvm_argv(CodeGen *g, const char **args, size_t len) {
- g->llvm_argv = args;
- g->llvm_argv_len = len;
-}
-
-void codegen_set_test_filter(CodeGen *g, Buf *filter) {
- g->test_filter = filter;
-}
-
-void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix) {
- g->test_name_prefix = prefix;
-}
-
-void codegen_set_lib_version(CodeGen *g, bool is_versioned, size_t major, size_t minor, size_t patch) {
- g->is_versioned = is_versioned;
- g->version_major = major;
- g->version_minor = minor;
- g->version_patch = patch;
-}
-
-void codegen_set_each_lib_rpath(CodeGen *g, bool each_lib_rpath) {
- g->each_lib_rpath = each_lib_rpath;
-}
-
-void codegen_set_errmsg_color(CodeGen *g, ErrColor err_color) {
- g->err_color = err_color;
-}
-
void codegen_set_strip(CodeGen *g, bool strip) {
g->strip_debug_symbols = strip;
if (!target_has_debug_info(g->zig_target)) {
@@ -113,39 +78,6 @@ void codegen_set_strip(CodeGen *g, bool strip) {
}
}
-void codegen_set_out_name(CodeGen *g, Buf *out_name) {
- g->root_out_name = out_name;
-}
-
-void codegen_add_lib_dir(CodeGen *g, const char *dir) {
- g->lib_dirs.append(dir);
-}
-
-void codegen_add_rpath(CodeGen *g, const char *name) {
- g->rpath_list.append(buf_create_from_str(name));
-}
-
-LinkLib *codegen_add_link_lib(CodeGen *g, Buf *name) {
- return add_link_lib(g, name);
-}
-
-void codegen_add_forbidden_lib(CodeGen *codegen, Buf *lib) {
- codegen->forbidden_libs.append(lib);
-}
-
-void codegen_add_framework(CodeGen *g, const char *framework) {
- g->darwin_frameworks.append(buf_create_from_str(framework));
-}
-
-void codegen_set_rdynamic(CodeGen *g, bool rdynamic) {
- g->linker_rdynamic = rdynamic;
-}
-
-void codegen_set_linker_script(CodeGen *g, const char *linker_script) {
- g->linker_script = linker_script;
-}
-
-
static void render_const_val(CodeGen *g, ZigValue *const_val, const char *name);
static void render_const_val_global(CodeGen *g, ZigValue *const_val, const char *name);
static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *name);
@@ -156,7 +88,6 @@ static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *na
static LLVMValueRef gen_await_early_return(CodeGen *g, IrInstGen *source_instr,
LLVMValueRef target_frame_ptr, ZigType *result_type, ZigType *ptr_result_type,
LLVMValueRef result_loc, bool non_async);
-static Error get_tmp_filename(CodeGen *g, Buf *out, Buf *suffix);
static void addLLVMAttr(LLVMValueRef val, LLVMAttributeIndex attr_index, const char *attr_name) {
unsigned kind_id = LLVMGetEnumAttributeKindForName(attr_name, strlen(attr_name));
@@ -390,7 +321,7 @@ static uint32_t get_err_ret_trace_arg_index(CodeGen *g, ZigFn *fn_table_entry) {
}
static void maybe_export_dll(CodeGen *g, LLVMValueRef global_value, GlobalLinkageId linkage) {
- if (linkage != GlobalLinkageIdInternal && g->zig_target->os == OsWindows && g->is_dynamic) {
+ if (linkage != GlobalLinkageIdInternal && g->zig_target->os == OsWindows && g->dll_export_fns) {
LLVMSetDLLStorageClass(global_value, LLVMDLLExportStorageClass);
}
}
@@ -542,7 +473,7 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) {
g->build_mode != BuildModeSmallRelease &&
!fn->def_scope->safety_off;
if (want_fn_safety) {
- if (g->libc_link_lib != nullptr) {
+ if (g->link_libc) {
addLLVMFnAttr(llvm_fn, "sspstrong");
addLLVMFnAttrStr(llvm_fn, "stack-protector-buffer-size", "4");
}
@@ -3860,20 +3791,6 @@ static LLVMValueRef gen_valgrind_client_request(CodeGen *g, LLVMValueRef default
zig_unreachable();
}
-static bool want_valgrind_support(CodeGen *g) {
- if (!target_has_valgrind_support(g->zig_target))
- return false;
- switch (g->valgrind_support) {
- case ValgrindSupportDisabled:
- return false;
- case ValgrindSupportEnabled:
- return true;
- case ValgrindSupportAuto:
- return g->build_mode == BuildModeDebug;
- }
- zig_unreachable();
-}
-
static void gen_valgrind_undef(CodeGen *g, LLVMValueRef dest_ptr, LLVMValueRef byte_count) {
static const uint32_t VG_USERREQ__MAKE_MEM_UNDEFINED = 1296236545;
ZigType *usize = g->builtin_types.entry_usize;
@@ -3896,7 +3813,7 @@ static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_
LLVMValueRef byte_count = LLVMConstInt(usize->llvm_type, size_bytes, false);
ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, ptr_align_bytes, false);
// then tell valgrind that the memory is undefined even though we just memset it
- if (want_valgrind_support(g)) {
+ if (g->valgrind_enabled) {
gen_valgrind_undef(g, dest_ptr, byte_count);
}
}
@@ -5599,7 +5516,7 @@ static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutableGen *executable, Ir
ZigLLVMBuildMemSet(g->builder, dest_ptr_casted, fill_char, len_val, get_ptr_align(g, ptr_type),
ptr_type->data.pointer.is_volatile);
- if (val_is_undef && want_valgrind_support(g)) {
+ if (val_is_undef && g->valgrind_enabled) {
gen_valgrind_undef(g, dest_ptr_casted, len_val);
}
return nullptr;
@@ -8327,9 +8244,9 @@ static void zig_llvm_emit_output(CodeGen *g) {
const char *bin_filename = nullptr;
const char *llvm_ir_filename = nullptr;
- if (g->emit_bin) bin_filename = buf_ptr(&g->o_file_output_path);
- if (g->emit_asm) asm_filename = buf_ptr(&g->asm_file_output_path);
- if (g->emit_llvm_ir) llvm_ir_filename = buf_ptr(&g->llvm_ir_file_output_path);
+ if (buf_len(&g->o_file_output_path) != 0) bin_filename = buf_ptr(&g->o_file_output_path);
+ if (buf_len(&g->asm_file_output_path) != 0) asm_filename = buf_ptr(&g->asm_file_output_path);
+ if (buf_len(&g->llvm_ir_file_output_path) != 0) llvm_ir_filename = buf_ptr(&g->llvm_ir_file_output_path);
// Unfortunately, LLVM shits the bed when we ask for both binary and assembly. So we call the entire
// pipeline multiple times if this is requested.
@@ -8351,13 +8268,6 @@ static void zig_llvm_emit_output(CodeGen *g) {
exit(1);
}
- if (g->emit_bin) {
- g->link_objects.append(&g->o_file_output_path);
- if (g->bundle_compiler_rt && (g->out_type == OutTypeObj || (g->out_type == OutTypeLib && !g->is_dynamic))) {
- zig_link_add_compiler_rt(g, g->sub_progress_node);
- }
- }
-
LLVMDisposeModule(g->module);
g->module = nullptr;
LLVMDisposeTargetData(g->target_data_ref);
@@ -8752,79 +8662,16 @@ static const char *subsystem_to_str(TargetSubsystem subsystem) {
zig_unreachable();
}
-static bool detect_dynamic_link(CodeGen *g) {
- if (g->is_dynamic)
- return true;
- if (g->zig_target->os == OsFreestanding)
- return false;
- if (target_os_requires_libc(g->zig_target->os))
- return true;
- if (g->libc_link_lib != nullptr && target_is_glibc(g->zig_target))
- return true;
- // If there are no dynamic libraries then we can disable dynamic linking.
- for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
- LinkLib *link_lib = g->link_libs_list.at(i);
- if (target_is_libc_lib_name(g->zig_target, buf_ptr(link_lib->name)))
- continue;
- if (target_is_libcpp_lib_name(g->zig_target, buf_ptr(link_lib->name)))
- continue;
- return true;
- }
- return false;
-}
-
-static bool detect_pic(CodeGen *g) {
- if (target_requires_pic(g->zig_target, g->libc_link_lib != nullptr))
- return true;
- switch (g->want_pic) {
- case WantPICDisabled:
- return false;
- case WantPICEnabled:
- return true;
- case WantPICAuto:
- return g->have_dynamic_link;
- }
- zig_unreachable();
-}
-
-static bool detect_stack_probing(CodeGen *g) {
- if (!target_supports_stack_probing(g->zig_target))
- return false;
- switch (g->want_stack_check) {
- case WantStackCheckDisabled:
- return false;
- case WantStackCheckEnabled:
- return true;
- case WantStackCheckAuto:
- return g->build_mode == BuildModeSafeRelease || g->build_mode == BuildModeDebug;
- }
- zig_unreachable();
-}
-
-static bool detect_sanitize_c(CodeGen *g) {
- if (!target_supports_sanitize_c(g->zig_target))
- return false;
- switch (g->want_sanitize_c) {
- case WantCSanitizeDisabled:
- return false;
- case WantCSanitizeEnabled:
- return true;
- case WantCSanitizeAuto:
- return g->build_mode == BuildModeSafeRelease || g->build_mode == BuildModeDebug;
- }
- zig_unreachable();
-}
-
// Returns TargetSubsystemAuto to mean "no subsystem"
TargetSubsystem detect_subsystem(CodeGen *g) {
if (g->subsystem != TargetSubsystemAuto)
return g->subsystem;
if (g->zig_target->os == OsWindows) {
- if (g->have_dllmain_crt_startup || (g->out_type == OutTypeLib && g->is_dynamic))
+ if (g->stage1.have_dllmain_crt_startup)
return TargetSubsystemAuto;
- if (g->have_c_main || g->is_test_build || g->have_winmain_crt_startup || g->have_wwinmain_crt_startup)
+ if (g->stage1.have_c_main || g->is_test_build || g->stage1.have_winmain_crt_startup || g->stage1.have_wwinmain_crt_startup)
return TargetSubsystemConsole;
- if (g->have_winmain || g->have_wwinmain)
+ if (g->stage1.have_winmain || g->stage1.have_wwinmain)
return TargetSubsystemWindows;
} else if (g->zig_target->os == OsUefi) {
return TargetSubsystemEfiApplication;
@@ -8832,15 +8679,6 @@ TargetSubsystem detect_subsystem(CodeGen *g) {
return TargetSubsystemAuto;
}
-static bool detect_single_threaded(CodeGen *g) {
- if (g->want_single_threaded)
- return true;
- if (target_is_single_threaded(g->zig_target)) {
- return true;
- }
- return false;
-}
-
static bool detect_err_ret_tracing(CodeGen *g) {
return !g->strip_debug_symbols &&
g->build_mode != BuildModeFastRelease &&
@@ -8848,30 +8686,30 @@ static bool detect_err_ret_tracing(CodeGen *g) {
}
static LLVMCodeModel to_llvm_code_model(CodeGen *g) {
- switch (g->code_model) {
- case CodeModelDefault:
- return LLVMCodeModelDefault;
- case CodeModelTiny:
- return LLVMCodeModelTiny;
- case CodeModelSmall:
- return LLVMCodeModelSmall;
- case CodeModelKernel:
- return LLVMCodeModelKernel;
- case CodeModelMedium:
- return LLVMCodeModelMedium;
- case CodeModelLarge:
- return LLVMCodeModelLarge;
- }
+ switch (g->code_model) {
+ case CodeModelDefault:
+ return LLVMCodeModelDefault;
+ case CodeModelTiny:
+ return LLVMCodeModelTiny;
+ case CodeModelSmall:
+ return LLVMCodeModelSmall;
+ case CodeModelKernel:
+ return LLVMCodeModelKernel;
+ case CodeModelMedium:
+ return LLVMCodeModelMedium;
+ case CodeModelLarge:
+ return LLVMCodeModelLarge;
+ }
- zig_unreachable();
+ zig_unreachable();
}
Buf *codegen_generate_builtin_source(CodeGen *g) {
- g->have_dynamic_link = detect_dynamic_link(g);
- g->have_pic = detect_pic(g);
- g->have_stack_probing = detect_stack_probing(g);
- g->have_sanitize_c = detect_sanitize_c(g);
- g->is_single_threaded = detect_single_threaded(g);
+ // Note that this only runs when zig0 is building the self-hosted zig compiler code,
+ // so it makes a few assumption that are always true for that case. Once we have
+ // built the stage2 zig components then zig is in charge of generating the builtin.zig
+ // file.
+
g->have_err_ret_tracing = detect_err_ret_tracing(g);
Buf *contents = buf_alloc();
@@ -8885,7 +8723,6 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
const char *name = target_os_name(os_type);
if (os_type == g->zig_target->os) {
- g->target_os_index = i;
cur_os = name;
}
}
@@ -8899,7 +8736,6 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
ZigLLVM_ArchType arch = target_arch_enum(arch_i);
const char *arch_name = target_arch_name(arch);
if (arch == g->zig_target->arch) {
- g->target_arch_index = arch_i;
cur_arch = arch_name;
}
}
@@ -8914,7 +8750,6 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
const char *name = target_abi_name(abi);
if (abi == g->zig_target->abi) {
- g->target_abi_index = i;
cur_abi = name;
}
}
@@ -8930,7 +8765,6 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
ZigLLVM_ObjectFormatType target_oformat = target_object_format(g->zig_target);
if (oformat == target_oformat) {
- g->target_oformat_index = i;
cur_obj_fmt = name;
}
}
@@ -8976,83 +8810,27 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
static_assert(TargetSubsystemEfiBootServiceDriver == 5, "");
static_assert(TargetSubsystemEfiRom == 6, "");
static_assert(TargetSubsystemEfiRuntimeDriver == 7, "");
- {
- const char *endian_str = g->is_big_endian ? "Endian.Big" : "Endian.Little";
- buf_appendf(contents, "pub const endian = %s;\n", endian_str);
- }
- const char *out_type = nullptr;
- switch (g->out_type) {
- case OutTypeExe:
- out_type = "Exe";
- break;
- case OutTypeLib:
- out_type = "Lib";
- break;
- case OutTypeObj:
- case OutTypeUnknown: // This happens when running the `zig builtin` command.
- out_type = "Obj";
- break;
- }
- buf_appendf(contents, "pub const output_mode = OutputMode.%s;\n", out_type);
- const char *link_type = g->have_dynamic_link ? "Dynamic" : "Static";
- buf_appendf(contents, "pub const link_mode = LinkMode.%s;\n", link_type);
- buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build));
+
+ buf_append_str(contents, "/// Deprecated: use `std.Target.current.cpu.arch`\n");
+ buf_append_str(contents, "pub const arch = Target.current.cpu.arch;\n");
+ buf_append_str(contents, "/// Deprecated: use `std.Target.current.cpu.arch.endian()`\n");
+ buf_append_str(contents, "pub const endian = Target.current.cpu.arch.endian();\n");
+ buf_appendf(contents, "pub const output_mode = OutputMode.Obj;\n");
+ buf_appendf(contents, "pub const link_mode = LinkMode.Static;\n");
+ buf_appendf(contents, "pub const is_test = false;\n");
buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded));
- buf_append_str(contents, "/// Deprecated: use `std.Target.cpu.arch`\n");
- buf_appendf(contents, "pub const arch = Arch.%s;\n", cur_arch);
buf_appendf(contents, "pub const abi = Abi.%s;\n", cur_abi);
- {
- buf_append_str(contents, "pub const cpu: Cpu = ");
- if (g->zig_target->cpu_builtin_str != nullptr) {
- buf_append_str(contents, g->zig_target->cpu_builtin_str);
- } else {
- buf_appendf(contents, "Target.Cpu.baseline(.%s);\n", cur_arch);
- }
- }
- {
- buf_append_str(contents, "pub const os = ");
- if (g->zig_target->os_builtin_str != nullptr) {
- buf_append_str(contents, g->zig_target->os_builtin_str);
- } else {
- buf_appendf(contents, "Target.Os.defaultVersionRange(.%s);\n", cur_os);
- }
- }
+ buf_appendf(contents, "pub const cpu: Cpu = Target.Cpu.baseline(.%s);\n", cur_arch);
+ buf_appendf(contents, "pub const os = Target.Os.Tag.defaultVersionRange(.%s);\n", cur_os);
buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt);
buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode));
- buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr));
- buf_appendf(contents, "pub const link_libcpp = %s;\n", bool_to_str(g->libcpp_link_lib != nullptr));
+ buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->link_libc));
+ buf_appendf(contents, "pub const link_libcpp = %s;\n", bool_to_str(g->link_libcpp));
buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing));
- buf_appendf(contents, "pub const valgrind_support = %s;\n", bool_to_str(want_valgrind_support(g)));
+ buf_appendf(contents, "pub const valgrind_support = false;\n");
buf_appendf(contents, "pub const position_independent_code = %s;\n", bool_to_str(g->have_pic));
buf_appendf(contents, "pub const strip_debug_info = %s;\n", bool_to_str(g->strip_debug_symbols));
-
- {
- const char *code_model;
- switch (g->code_model) {
- case CodeModelDefault:
- code_model = "default";
- break;
- case CodeModelTiny:
- code_model = "tiny";
- break;
- case CodeModelSmall:
- code_model = "small";
- break;
- case CodeModelKernel:
- code_model = "kernel";
- break;
- case CodeModelMedium:
- code_model = "medium";
- break;
- case CodeModelLarge:
- code_model = "large";
- break;
- default:
- zig_unreachable();
- }
-
- buf_appendf(contents, "pub const code_model = CodeModel.%s;\n", code_model);
- }
+ buf_appendf(contents, "pub const code_model = CodeModel.default;\n");
{
TargetSubsystem detected_subsystem = detect_subsystem(g);
@@ -9061,15 +8839,6 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
}
}
- if (g->is_test_build) {
- buf_appendf(contents,
- "pub var test_functions: []TestFn = undefined; // overwritten later\n"
- );
-
- buf_appendf(contents, "pub const test_io_mode = %s;\n",
- g->test_is_evented ? ".evented" : ".blocking");
- }
-
return contents;
}
@@ -9078,95 +8847,47 @@ static ZigPackage *create_test_runner_pkg(CodeGen *g) {
}
static Error define_builtin_compile_vars(CodeGen *g) {
+ Error err;
+
if (g->std_package == nullptr)
return ErrorNone;
- Error err;
-
- Buf *manifest_dir = buf_alloc();
- os_path_join(get_global_cache_dir(), buf_create_from_str("builtin"), manifest_dir);
-
- CacheHash cache_hash;
- cache_init(&cache_hash, manifest_dir);
-
- Buf *compiler_id;
- if ((err = get_compiler_id(&compiler_id)))
- return err;
-
- // Only a few things affect builtin.zig
- cache_buf(&cache_hash, compiler_id);
- cache_int(&cache_hash, g->build_mode);
- cache_bool(&cache_hash, g->strip_debug_symbols);
- cache_int(&cache_hash, g->out_type);
- cache_bool(&cache_hash, detect_dynamic_link(g));
- cache_bool(&cache_hash, g->is_test_build);
- cache_bool(&cache_hash, g->is_single_threaded);
- cache_bool(&cache_hash, g->test_is_evented);
- cache_int(&cache_hash, g->code_model);
- cache_int(&cache_hash, g->zig_target->is_native_os);
- cache_int(&cache_hash, g->zig_target->is_native_cpu);
- cache_int(&cache_hash, g->zig_target->arch);
- cache_int(&cache_hash, g->zig_target->vendor);
- cache_int(&cache_hash, g->zig_target->os);
- cache_int(&cache_hash, g->zig_target->abi);
- if (g->zig_target->cache_hash != nullptr) {
- cache_mem(&cache_hash, g->zig_target->cache_hash, g->zig_target->cache_hash_len);
- }
- if (g->zig_target->glibc_or_darwin_version != nullptr) {
- cache_int(&cache_hash, g->zig_target->glibc_or_darwin_version->major);
- cache_int(&cache_hash, g->zig_target->glibc_or_darwin_version->minor);
- cache_int(&cache_hash, g->zig_target->glibc_or_darwin_version->patch);
- }
- cache_bool(&cache_hash, g->have_err_ret_tracing);
- cache_bool(&cache_hash, g->libc_link_lib != nullptr);
- cache_bool(&cache_hash, g->libcpp_link_lib != nullptr);
- cache_bool(&cache_hash, g->valgrind_support);
- cache_bool(&cache_hash, g->link_eh_frame_hdr);
- cache_int(&cache_hash, detect_subsystem(g));
-
- Buf digest = BUF_INIT;
- buf_resize(&digest, 0);
- if ((err = cache_hit(&cache_hash, &digest))) {
- // Treat an invalid format error as a cache miss.
- if (err != ErrorInvalidFormat)
- return err;
- }
-
- // We should always get a cache hit because there are no
- // files in the input hash.
- assert(buf_len(&digest) != 0);
-
- Buf *this_dir = buf_alloc();
- os_path_join(manifest_dir, &digest, this_dir);
-
- if ((err = os_make_path(this_dir)))
- return err;
+ assert(g->main_pkg);
const char *builtin_zig_basename = "builtin.zig";
- Buf *builtin_zig_path = buf_alloc();
- os_path_join(this_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path);
- bool hit;
- if ((err = os_file_exists(builtin_zig_path, &hit)))
- return err;
Buf *contents;
- if (hit) {
- contents = buf_alloc();
- if ((err = os_fetch_file_path(builtin_zig_path, contents))) {
- fprintf(stderr, "Unable to open '%s': %s\n", buf_ptr(builtin_zig_path), err_str(err));
- exit(1);
- }
- } else {
+ if (g->builtin_zig_path == nullptr) {
+ // Then this is zig0 building stage2. We can make many assumptions about the compilation.
+ Buf *out_dir = buf_alloc();
+ os_path_split(&g->o_file_output_path, out_dir, nullptr);
+ g->builtin_zig_path = buf_alloc();
+ os_path_join(out_dir, buf_create_from_str(builtin_zig_basename), g->builtin_zig_path);
+
+ Buf *resolve_paths[] = { g->builtin_zig_path, };
+ *g->builtin_zig_path = os_path_resolve(resolve_paths, 1);
+
contents = codegen_generate_builtin_source(g);
- if ((err = os_write_file(builtin_zig_path, contents))) {
- fprintf(stderr, "Unable to write file '%s': %s\n", buf_ptr(builtin_zig_path), err_str(err));
+ if ((err = os_write_file(g->builtin_zig_path, contents))) {
+ fprintf(stderr, "Unable to write file '%s': %s\n", buf_ptr(g->builtin_zig_path), err_str(err));
exit(1);
}
+
+ g->compile_var_package = new_package(buf_ptr(out_dir), builtin_zig_basename, "builtin");
+ } else {
+ Buf *resolve_paths[] = { g->builtin_zig_path, };
+ *g->builtin_zig_path = os_path_resolve(resolve_paths, 1);
+
+ contents = buf_alloc();
+ if ((err = os_fetch_file_path(g->builtin_zig_path, contents))) {
+ fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(g->builtin_zig_path), err_str(err));
+ exit(1);
+ }
+ Buf builtin_dirname = BUF_INIT;
+ os_path_dirname(g->builtin_zig_path, &builtin_dirname);
+ g->compile_var_package = new_package(buf_ptr(&builtin_dirname), builtin_zig_basename, "builtin");
}
- assert(g->main_pkg);
- assert(g->std_package);
- g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename, "builtin");
if (g->is_test_build) {
if (g->test_runner_package == nullptr) {
g->test_runner_package = create_test_runner_pkg(g);
@@ -9181,7 +8902,7 @@ static Error define_builtin_compile_vars(CodeGen *g) {
g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
g->std_package->package_table.put(buf_create_from_str("std"), g->std_package);
g->std_package->package_table.put(buf_create_from_str("root"), g->root_pkg);
- g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents,
+ g->compile_var_import = add_source_file(g, g->compile_var_package, g->builtin_zig_path, contents,
SourceKindPkgMain);
return ErrorNone;
@@ -9191,17 +8912,15 @@ static void init(CodeGen *g) {
if (g->module)
return;
- g->have_dynamic_link = detect_dynamic_link(g);
- g->have_pic = detect_pic(g);
- g->have_stack_probing = detect_stack_probing(g);
- g->have_sanitize_c = detect_sanitize_c(g);
- g->is_single_threaded = detect_single_threaded(g);
- g->have_err_ret_tracing = detect_err_ret_tracing(g);
-
- if (target_is_single_threaded(g->zig_target)) {
- g->is_single_threaded = true;
+ codegen_add_time_event(g, "Initialize");
+ {
+ const char *progress_name = "Initialize";
+ codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node,
+ progress_name, strlen(progress_name), 0));
}
+ g->have_err_ret_tracing = detect_err_ret_tracing(g);
+
assert(g->root_out_name);
g->module = LLVMModuleCreateWithName(buf_ptr(g->root_out_name));
@@ -9231,7 +8950,7 @@ static void init(CodeGen *g) {
LLVMRelocMode reloc_mode;
if (g->have_pic) {
reloc_mode = LLVMRelocPIC;
- } else if (g->have_dynamic_link) {
+ } else if (g->link_mode_dynamic) {
reloc_mode = LLVMRelocDynamicNoPic;
} else {
reloc_mode = LLVMRelocStatic;
@@ -9337,446 +9056,6 @@ static void init(CodeGen *g) {
}
}
-static void detect_libc(CodeGen *g) {
- Error err;
-
- if (g->libc != nullptr || g->libc_link_lib == nullptr)
- return;
-
- if (target_can_build_libc(g->zig_target)) {
- const char *generic_name = target_libc_generic_name(g->zig_target);
- const char *arch_name = target_arch_name(g->zig_target->arch);
- const char *abi_name = target_abi_name(g->zig_target->abi);
- if (target_is_musl(g->zig_target)) {
- // musl has some overrides. its headers are ABI-agnostic and so they all have the "musl" ABI name.
- abi_name = "musl";
- // some architectures are handled by the same set of headers
- arch_name = target_arch_musl_name(g->zig_target->arch);
- }
- Buf *arch_include_dir = buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "%s-%s-%s",
- buf_ptr(g->zig_lib_dir), arch_name, target_os_name(g->zig_target->os), abi_name);
- Buf *generic_include_dir = buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "generic-%s",
- buf_ptr(g->zig_lib_dir), generic_name);
- Buf *arch_os_include_dir = buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "%s-%s-any",
- buf_ptr(g->zig_lib_dir), target_arch_name(g->zig_target->arch), target_os_name(g->zig_target->os));
- Buf *generic_os_include_dir = buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "any-%s-any",
- buf_ptr(g->zig_lib_dir), target_os_name(g->zig_target->os));
-
- g->libc_include_dir_len = 4;
- g->libc_include_dir_list = heap::c_allocator.allocate(g->libc_include_dir_len);
- g->libc_include_dir_list[0] = buf_ptr(arch_include_dir);
- g->libc_include_dir_list[1] = buf_ptr(generic_include_dir);
- g->libc_include_dir_list[2] = buf_ptr(arch_os_include_dir);
- g->libc_include_dir_list[3] = buf_ptr(generic_os_include_dir);
- return;
- }
-
- if (g->zig_target->is_native_os) {
- g->libc = heap::c_allocator.create();
-
- if ((err = stage2_libc_find_native(g->libc))) {
- fprintf(stderr,
- "Unable to link against libc: Unable to find libc installation: %s\n"
- "See `zig libc --help` for more details.\n", err_str(err));
- exit(1);
- }
-
- bool want_sys_dir = !mem_eql_mem(g->libc->include_dir, g->libc->include_dir_len,
- g->libc->sys_include_dir, g->libc->sys_include_dir_len);
- size_t want_um_and_shared_dirs = (g->zig_target->os == OsWindows) ? 2 : 0;
- size_t dir_count = 1 + want_sys_dir + want_um_and_shared_dirs;
- g->libc_include_dir_len = 0;
- g->libc_include_dir_list = heap::c_allocator.allocate(dir_count);
-
- g->libc_include_dir_list[g->libc_include_dir_len] = buf_ptr(buf_create_from_mem(
- g->libc->include_dir, g->libc->include_dir_len));
- g->libc_include_dir_len += 1;
-
- if (want_sys_dir) {
- g->libc_include_dir_list[g->libc_include_dir_len] = buf_ptr(buf_create_from_mem(
- g->libc->sys_include_dir, g->libc->sys_include_dir_len));
- g->libc_include_dir_len += 1;
- }
-
- if (want_um_and_shared_dirs != 0) {
- Buf *include_dir_parent = buf_alloc();
- os_path_join(buf_create_from_mem(g->libc->include_dir, g->libc->include_dir_len),
- buf_create_from_str(".."), include_dir_parent);
-
- Buf *buff1 = buf_alloc();
- os_path_join(include_dir_parent, buf_create_from_str("um"), buff1);
- g->libc_include_dir_list[g->libc_include_dir_len] = buf_ptr(buff1);
- g->libc_include_dir_len += 1;
-
- Buf *buff2 = buf_alloc();
- os_path_join(include_dir_parent, buf_create_from_str("shared"), buff2);
- g->libc_include_dir_list[g->libc_include_dir_len] = buf_ptr(buff2);
- g->libc_include_dir_len += 1;
- }
- assert(g->libc_include_dir_len == dir_count);
- } else if ((g->out_type == OutTypeExe || (g->out_type == OutTypeLib && g->is_dynamic)) &&
- !target_os_is_darwin(g->zig_target->os))
- {
- Buf triple_buf = BUF_INIT;
- target_triple_zig(&triple_buf, g->zig_target);
- fprintf(stderr,
- "Zig is unable to provide a libc for the chosen target '%s'.\n"
- "The target is non-native, so Zig also cannot use the native libc installation.\n"
- "Choose a target which has a libc available (see `zig targets`), or\n"
- "provide a libc installation text file (see `zig libc --help`).\n", buf_ptr(&triple_buf));
- exit(1);
- }
-}
-
-// does not add the "cc" arg
-void add_cc_args(CodeGen *g, ZigList &args, const char *out_dep_path,
- bool translate_c, FileExt source_kind)
-{
- if (translate_c) {
- args.append("-x");
- args.append("c");
- }
-
- args.append("-nostdinc");
- if (source_kind == FileExtCpp) {
- args.append("-nostdinc++");
- }
- args.append("-fno-spell-checking");
-
- if (g->function_sections) {
- args.append("-ffunction-sections");
- }
-
- if (!translate_c) {
- switch (g->err_color) {
- case ErrColorAuto:
- break;
- case ErrColorOff:
- args.append("-fno-color-diagnostics");
- args.append("-fno-caret-diagnostics");
- break;
- case ErrColorOn:
- args.append("-fcolor-diagnostics");
- args.append("-fcaret-diagnostics");
- break;
- }
- }
-
- for (size_t i = 0; i < g->framework_dirs.length; i += 1) {
- args.append("-iframework");
- args.append(g->framework_dirs.at(i));
- }
-
- if (g->libcpp_link_lib != nullptr) {
- const char *libcxx_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "include",
- buf_ptr(g->zig_lib_dir)));
-
- const char *libcxxabi_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxxabi" OS_SEP "include",
- buf_ptr(g->zig_lib_dir)));
-
- args.append("-isystem");
- args.append(libcxx_include_path);
-
- args.append("-isystem");
- args.append(libcxxabi_include_path);
-
- if (target_abi_is_musl(g->zig_target->abi)) {
- args.append("-D_LIBCPP_HAS_MUSL_LIBC");
- }
- args.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS");
- args.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS");
- }
-
- args.append("-target");
- args.append(buf_ptr(&g->llvm_triple_str));
-
- switch (source_kind) {
- case FileExtC:
- case FileExtCpp:
- case FileExtHeader:
- // According to Rich Felker libc headers are supposed to go before C language headers.
- // However as noted by @dimenus, appending libc headers before c_headers breaks intrinsics
- // and other compiler specific items.
- args.append("-isystem");
- args.append(buf_ptr(g->zig_c_headers_dir));
-
- for (size_t i = 0; i < g->libc_include_dir_len; i += 1) {
- const char *include_dir = g->libc_include_dir_list[i];
- args.append("-isystem");
- args.append(include_dir);
- }
-
- if (g->zig_target->llvm_cpu_name != nullptr) {
- args.append("-Xclang");
- args.append("-target-cpu");
- args.append("-Xclang");
- args.append(g->zig_target->llvm_cpu_name);
- }
- if (g->zig_target->llvm_cpu_features != nullptr) {
- // https://github.com/ziglang/zig/issues/5017
- SplitIterator it = memSplit(str(g->zig_target->llvm_cpu_features), str(","));
- Optional> flag = SplitIterator_next(&it);
- while (flag.is_some) {
- args.append("-Xclang");
- args.append("-target-feature");
- args.append("-Xclang");
- args.append(buf_ptr(buf_create_from_slice(flag.value)));
- flag = SplitIterator_next(&it);
- }
- }
- if (translate_c) {
- // this gives us access to preprocessing entities, presumably at
- // the cost of performance
- args.append("-Xclang");
- args.append("-detailed-preprocessing-record");
- }
- if (out_dep_path != nullptr) {
- args.append("-MD");
- args.append("-MV");
- args.append("-MF");
- args.append(out_dep_path);
- }
- break;
- case FileExtAsm:
- case FileExtLLVMIr:
- case FileExtLLVMBitCode:
- case FileExtUnknown:
- break;
- }
- for (size_t i = 0; i < g->zig_target->llvm_cpu_features_asm_len; i += 1) {
- args.append(g->zig_target->llvm_cpu_features_asm_ptr[i]);
- }
-
- if (g->zig_target->os == OsFreestanding) {
- args.append("-ffreestanding");
- }
-
- // windows.h has files such as pshpack1.h which do #pragma packing, triggering a clang warning.
- // So for this target, we disable this warning.
- if (g->zig_target->os == OsWindows && target_abi_is_gnu(g->zig_target->abi)) {
- args.append("-Wno-pragma-pack");
- }
-
- if (!g->strip_debug_symbols) {
- args.append("-g");
- }
-
- if (codegen_have_frame_pointer(g)) {
- args.append("-fno-omit-frame-pointer");
- } else {
- args.append("-fomit-frame-pointer");
- }
-
- if (g->have_sanitize_c) {
- args.append("-fsanitize=undefined");
- args.append("-fsanitize-trap=undefined");
- }
-
- switch (g->build_mode) {
- case BuildModeDebug:
- // windows c runtime requires -D_DEBUG if using debug libraries
- args.append("-D_DEBUG");
- args.append("-Og");
-
- if (g->libc_link_lib != nullptr) {
- args.append("-fstack-protector-strong");
- args.append("--param");
- args.append("ssp-buffer-size=4");
- } else {
- args.append("-fno-stack-protector");
- }
- break;
- case BuildModeSafeRelease:
- // See the comment in the BuildModeFastRelease case for why we pass -O2 rather
- // than -O3 here.
- args.append("-O2");
- if (g->libc_link_lib != nullptr) {
- args.append("-D_FORTIFY_SOURCE=2");
- args.append("-fstack-protector-strong");
- args.append("--param");
- args.append("ssp-buffer-size=4");
- } else {
- args.append("-fno-stack-protector");
- }
- break;
- case BuildModeFastRelease:
- args.append("-DNDEBUG");
- // Here we pass -O2 rather than -O3 because, although we do the equivalent of
- // -O3 in Zig code, the justification for the difference here is that Zig
- // has better detection and prevention of undefined behavior, so -O3 is safer for
- // Zig code than it is for C code. Also, C programmers are used to their code
- // running in -O2 and thus the -O3 path has been tested less.
- args.append("-O2");
- args.append("-fno-stack-protector");
- break;
- case BuildModeSmallRelease:
- args.append("-DNDEBUG");
- args.append("-Os");
- args.append("-fno-stack-protector");
- break;
- }
-
- if (target_supports_fpic(g->zig_target) && g->have_pic) {
- args.append("-fPIC");
- }
-
- for (size_t arg_i = 0; arg_i < g->clang_argv_len; arg_i += 1) {
- args.append(g->clang_argv[arg_i]);
- }
-
-}
-
-void codegen_translate_c(CodeGen *g, Buf *full_path) {
- Error err;
-
- Buf *src_basename = buf_alloc();
- Buf *src_dirname = buf_alloc();
- os_path_split(full_path, src_dirname, src_basename);
-
- Buf noextname = BUF_INIT;
- os_path_extname(src_basename, &noextname, nullptr);
-
- Buf *zig_basename = buf_sprintf("%s.zig", buf_ptr(&noextname));
-
- detect_libc(g);
-
- Buf cache_digest = BUF_INIT;
- buf_resize(&cache_digest, 0);
-
- CacheHash *cache_hash = nullptr;
- if (g->enable_cache) {
- if ((err = create_c_object_cache(g, &cache_hash, true))) {
- // Already printed error; verbose = true
- exit(1);
- }
- cache_file(cache_hash, full_path);
- // to distinguish from generating a C object
- cache_buf(cache_hash, buf_create_from_str("translate-c"));
-
- if ((err = cache_hit(cache_hash, &cache_digest))) {
- if (err != ErrorInvalidFormat) {
- fprintf(stderr, "unable to check cache: %s\n", err_str(err));
- exit(1);
- }
- }
- if (cache_hash->manifest_file_path != nullptr) {
- g->caches_to_release.append(cache_hash);
- }
- }
-
- if (g->enable_cache && buf_len(&cache_digest) != 0) {
- // cache hit
- Buf *cached_path = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s" OS_SEP "%s",
- buf_ptr(g->cache_dir), buf_ptr(&cache_digest), buf_ptr(zig_basename));
- fprintf(stdout, "%s\n", buf_ptr(cached_path));
- return;
- }
-
- // cache miss or cache disabled
- init(g);
-
- Buf *out_dep_path = nullptr;
- const char *out_dep_path_cstr = nullptr;
-
- if (g->enable_cache) {
- buf_alloc();// we can't know the digest until we do the C compiler invocation, so we
- // need a tmp filename.
- out_dep_path = buf_alloc();
- if ((err = get_tmp_filename(g, out_dep_path, buf_sprintf("%s.d", buf_ptr(zig_basename))))) {
- fprintf(stderr, "unable to create tmp dir: %s\n", err_str(err));
- exit(1);
- }
- out_dep_path_cstr = buf_ptr(out_dep_path);
- }
-
- ZigList clang_argv = {0};
- add_cc_args(g, clang_argv, out_dep_path_cstr, true, FileExtC);
-
- clang_argv.append(buf_ptr(full_path));
-
- if (g->verbose_cc) {
- fprintf(stderr, "clang");
- for (size_t i = 0; i < clang_argv.length; i += 1) {
- fprintf(stderr, " %s", clang_argv.at(i));
- }
- fprintf(stderr, "\n");
- }
-
- clang_argv.append(nullptr); // to make the [start...end] argument work
-
- const char *resources_path = buf_ptr(g->zig_c_headers_dir);
- Stage2ErrorMsg *errors_ptr;
- size_t errors_len;
- Stage2Ast *ast;
-
- err = stage2_translate_c(&ast, &errors_ptr, &errors_len,
- &clang_argv.at(0), &clang_argv.last(), resources_path);
-
- if (err == ErrorCCompileErrors && errors_len > 0) {
- for (size_t i = 0; i < errors_len; i += 1) {
- Stage2ErrorMsg *clang_err = &errors_ptr[i];
-
- ErrorMsg *err_msg = err_msg_create_with_offset(
- clang_err->filename_ptr ?
- buf_create_from_mem(clang_err->filename_ptr, clang_err->filename_len) : nullptr,
- clang_err->line, clang_err->column, clang_err->offset, clang_err->source,
- buf_create_from_mem(clang_err->msg_ptr, clang_err->msg_len));
- print_err_msg(err_msg, g->err_color);
- }
- exit(1);
- }
-
- if (err) {
- fprintf(stderr, "unable to parse C file: %s\n", err_str(err));
- exit(1);
- }
-
- if (!g->enable_cache) {
- stage2_render_ast(ast, stdout);
- return;
- }
-
- // add the files depended on to the cache system
- if ((err = cache_add_dep_file(cache_hash, out_dep_path, true))) {
- // Don't treat the absence of the .d file as a fatal error, the
- // compiler may not produce one eg. when compiling .s files
- if (err != ErrorFileNotFound) {
- fprintf(stderr, "Failed to add C source dependencies to cache: %s\n", err_str(err));
- exit(1);
- }
- }
- if (err != ErrorFileNotFound) {
- os_delete_file(out_dep_path);
- }
-
- if ((err = cache_final(cache_hash, &cache_digest))) {
- fprintf(stderr, "Unable to finalize cache hash: %s\n", err_str(err));
- exit(1);
- }
-
- Buf *artifact_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s",
- buf_ptr(g->cache_dir), buf_ptr(&cache_digest));
-
- if ((err = os_make_path(artifact_dir))) {
- fprintf(stderr, "Unable to make dir: %s\n", err_str(err));
- exit(1);
- }
-
- Buf *cached_path = buf_sprintf("%s" OS_SEP "%s", buf_ptr(artifact_dir), buf_ptr(zig_basename));
-
- FILE *out_file = fopen(buf_ptr(cached_path), "wb");
- if (out_file == nullptr) {
- fprintf(stderr, "Unable to open output file: %s\n", strerror(errno));
- exit(1);
- }
- stage2_render_ast(ast, out_file);
- if (fclose(out_file) != 0) {
- fprintf(stderr, "Unable to write to output file: %s\n", strerror(errno));
- exit(1);
- }
- fprintf(stdout, "%s\n", buf_ptr(cached_path));
-}
-
static void update_test_functions_builtin_decl(CodeGen *g) {
Error err;
@@ -9876,49 +9155,46 @@ static void gen_root_source(CodeGen *g) {
assert(root_import_alias == g->root_import);
assert(g->root_out_name);
- assert(g->out_type != OutTypeUnknown);
- if (!g->is_dummy_so) {
- // Zig has lazy top level definitions. Here we semantically analyze the panic function.
- Buf *import_target_path;
- Buf full_path = BUF_INIT;
- ZigType *std_import;
- if ((err = analyze_import(g, g->root_import, buf_create_from_str("std"), &std_import,
- &import_target_path, &full_path)))
- {
- if (err == ErrorFileNotFound) {
- fprintf(stderr, "unable to find '%s'", buf_ptr(import_target_path));
- } else {
- fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(&full_path), err_str(err));
- }
- exit(1);
+ // Zig has lazy top level definitions. Here we semantically analyze the panic function.
+ Buf *import_target_path;
+ Buf full_path = BUF_INIT;
+ ZigType *std_import;
+ if ((err = analyze_import(g, g->root_import, buf_create_from_str("std"), &std_import,
+ &import_target_path, &full_path)))
+ {
+ if (err == ErrorFileNotFound) {
+ fprintf(stderr, "unable to find '%s'", buf_ptr(import_target_path));
+ } else {
+ fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(&full_path), err_str(err));
}
-
- Tld *builtin_tld = find_decl(g, &get_container_scope(std_import)->base,
- buf_create_from_str("builtin"));
- assert(builtin_tld != nullptr);
- resolve_top_level_decl(g, builtin_tld, nullptr, false);
- report_errors_and_maybe_exit(g);
- assert(builtin_tld->id == TldIdVar);
- TldVar *builtin_tld_var = (TldVar*)builtin_tld;
- ZigValue *builtin_val = builtin_tld_var->var->const_value;
- assert(builtin_val->type->id == ZigTypeIdMetaType);
- ZigType *builtin_type = builtin_val->data.x_type;
-
- Tld *panic_tld = find_decl(g, &get_container_scope(builtin_type)->base,
- buf_create_from_str("panic"));
- assert(panic_tld != nullptr);
- resolve_top_level_decl(g, panic_tld, nullptr, false);
- report_errors_and_maybe_exit(g);
- assert(panic_tld->id == TldIdVar);
- TldVar *panic_tld_var = (TldVar*)panic_tld;
- ZigValue *panic_fn_val = panic_tld_var->var->const_value;
- assert(panic_fn_val->type->id == ZigTypeIdFn);
- assert(panic_fn_val->data.x_ptr.special == ConstPtrSpecialFunction);
- g->panic_fn = panic_fn_val->data.x_ptr.data.fn.fn_entry;
- assert(g->panic_fn != nullptr);
+ exit(1);
}
+ Tld *builtin_tld = find_decl(g, &get_container_scope(std_import)->base,
+ buf_create_from_str("builtin"));
+ assert(builtin_tld != nullptr);
+ resolve_top_level_decl(g, builtin_tld, nullptr, false);
+ report_errors_and_maybe_exit(g);
+ assert(builtin_tld->id == TldIdVar);
+ TldVar *builtin_tld_var = (TldVar*)builtin_tld;
+ ZigValue *builtin_val = builtin_tld_var->var->const_value;
+ assert(builtin_val->type->id == ZigTypeIdMetaType);
+ ZigType *builtin_type = builtin_val->data.x_type;
+
+ Tld *panic_tld = find_decl(g, &get_container_scope(builtin_type)->base,
+ buf_create_from_str("panic"));
+ assert(panic_tld != nullptr);
+ resolve_top_level_decl(g, panic_tld, nullptr, false);
+ report_errors_and_maybe_exit(g);
+ assert(panic_tld->id == TldIdVar);
+ TldVar *panic_tld_var = (TldVar*)panic_tld;
+ ZigValue *panic_fn_val = panic_tld_var->var->const_value;
+ assert(panic_fn_val->type->id == ZigTypeIdFn);
+ assert(panic_fn_val->data.x_ptr.special == ConstPtrSpecialFunction);
+ g->panic_fn = panic_fn_val->data.x_ptr.data.fn.fn_entry;
+ assert(g->panic_fn != nullptr);
+
if (!g->error_during_imports) {
semantic_analyze(g);
}
@@ -9935,781 +9211,6 @@ static void gen_root_source(CodeGen *g) {
}
-static void print_zig_cc_cmd(ZigList *args) {
- for (size_t arg_i = 0; arg_i < args->length; arg_i += 1) {
- const char *space_str = (arg_i == 0) ? "" : " ";
- fprintf(stderr, "%s%s", space_str, args->at(arg_i));
- }
- fprintf(stderr, "\n");
-}
-
-// Caller should delete the file when done or rename it into a better location.
-static Error get_tmp_filename(CodeGen *g, Buf *out, Buf *suffix) {
- Error err;
- buf_resize(out, 0);
- os_path_join(g->cache_dir, buf_create_from_str("tmp" OS_SEP), out);
- if ((err = os_make_path(out))) {
- return err;
- }
- const char base64[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
- assert(array_length(base64) == 64 + 1);
- for (size_t i = 0; i < 12; i += 1) {
- buf_append_char(out, base64[rand() % 64]);
- }
- buf_append_char(out, '-');
- buf_append_buf(out, suffix);
- return ErrorNone;
-}
-
-Error create_c_object_cache(CodeGen *g, CacheHash **out_cache_hash, bool verbose) {
- Error err;
- CacheHash *cache_hash = heap::c_allocator.create();
- Buf *manifest_dir = buf_sprintf("%s" OS_SEP CACHE_HASH_SUBDIR, buf_ptr(g->cache_dir));
- cache_init(cache_hash, manifest_dir);
-
- Buf *compiler_id;
- if ((err = get_compiler_id(&compiler_id))) {
- if (verbose) {
- fprintf(stderr, "unable to get compiler id: %s\n", err_str(err));
- }
- return err;
- }
- cache_buf(cache_hash, compiler_id);
- cache_int(cache_hash, g->err_color);
- cache_list_of_str(cache_hash, g->framework_dirs.items, g->framework_dirs.length);
- cache_bool(cache_hash, g->libcpp_link_lib != nullptr);
- cache_buf(cache_hash, g->zig_lib_dir);
- cache_buf(cache_hash, g->zig_c_headers_dir);
- cache_list_of_str(cache_hash, g->libc_include_dir_list, g->libc_include_dir_len);
- cache_int(cache_hash, g->zig_target->is_native_os);
- cache_int(cache_hash, g->zig_target->is_native_cpu);
- cache_int(cache_hash, g->zig_target->arch);
- cache_int(cache_hash, g->zig_target->vendor);
- cache_int(cache_hash, g->zig_target->os);
- cache_int(cache_hash, g->zig_target->abi);
- cache_bool(cache_hash, g->strip_debug_symbols);
- cache_int(cache_hash, g->build_mode);
- cache_bool(cache_hash, g->have_pic);
- cache_bool(cache_hash, g->have_sanitize_c);
- cache_bool(cache_hash, want_valgrind_support(g));
- cache_bool(cache_hash, g->function_sections);
- cache_int(cache_hash, g->code_model);
- cache_bool(cache_hash, codegen_have_frame_pointer(g));
- cache_bool(cache_hash, g->libc_link_lib);
- if (g->zig_target->cache_hash != nullptr) {
- cache_mem(cache_hash, g->zig_target->cache_hash, g->zig_target->cache_hash_len);
- }
-
- for (size_t arg_i = 0; arg_i < g->clang_argv_len; arg_i += 1) {
- cache_str(cache_hash, g->clang_argv[arg_i]);
- }
-
- *out_cache_hash = cache_hash;
- return ErrorNone;
-}
-
-static bool need_llvm_module(CodeGen *g) {
- return buf_len(&g->main_pkg->root_src_path) != 0;
-}
-
-// before gen_c_objects
-static bool main_output_dir_is_just_one_c_object_pre(CodeGen *g) {
- return g->enable_cache && g->c_source_files.length == 1 && !need_llvm_module(g) &&
- g->out_type == OutTypeObj && g->link_objects.length == 0;
-}
-
-// after gen_c_objects
-static bool main_output_dir_is_just_one_c_object_post(CodeGen *g) {
- return g->enable_cache && g->link_objects.length == 1 && !need_llvm_module(g) && g->out_type == OutTypeObj;
-}
-
-// returns true if it was a cache miss
-static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) {
- Error err;
-
- Buf *artifact_dir;
- Buf *o_final_path;
-
- Buf *o_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR, buf_ptr(g->cache_dir));
-
- Buf *c_source_file = buf_create_from_str(c_file->source_path);
- Buf *c_source_basename = buf_alloc();
- os_path_split(c_source_file, nullptr, c_source_basename);
-
- Stage2ProgressNode *child_prog_node = stage2_progress_start(g->sub_progress_node, buf_ptr(c_source_basename),
- buf_len(c_source_basename), 0);
-
- Buf *final_o_basename = buf_alloc();
- if (c_file->preprocessor_only_basename == nullptr) {
- // We special case when doing build-obj for just one C file
- if (main_output_dir_is_just_one_c_object_pre(g)) {
- buf_init_from_buf(final_o_basename, g->root_out_name);
- } else {
- os_path_extname(c_source_basename, final_o_basename, nullptr);
- }
- buf_append_str(final_o_basename, target_o_file_ext(g->zig_target));
- } else {
- buf_init_from_str(final_o_basename, c_file->preprocessor_only_basename);
- }
-
- CacheHash *cache_hash;
- if ((err = create_c_object_cache(g, &cache_hash, true))) {
- // Already printed error; verbose = true
- exit(1);
- }
- cache_file(cache_hash, c_source_file);
-
- // Note: not directory args, just args that always have a file next
- static const char *file_args[] = {
- "-include",
- };
- for (size_t arg_i = 0; arg_i < c_file->args.length; arg_i += 1) {
- const char *arg = c_file->args.at(arg_i);
- cache_str(cache_hash, arg);
- for (size_t file_arg_i = 0; file_arg_i < array_length(file_args); file_arg_i += 1) {
- if (strcmp(arg, file_args[file_arg_i]) == 0 && arg_i + 1 < c_file->args.length) {
- arg_i += 1;
- cache_file(cache_hash, buf_create_from_str(c_file->args.at(arg_i)));
- }
- }
- }
-
- Buf digest = BUF_INIT;
- buf_resize(&digest, 0);
- if ((err = cache_hit(cache_hash, &digest))) {
- if (err != ErrorInvalidFormat) {
- if (err == ErrorCacheUnavailable) {
- // already printed error
- } else {
- fprintf(stderr, "unable to check cache when compiling C object: %s\n", err_str(err));
- }
- exit(1);
- }
- }
- bool is_cache_miss = g->disable_c_depfile || (buf_len(&digest) == 0);
- if (is_cache_miss) {
- // we can't know the digest until we do the C compiler invocation, so we
- // need a tmp filename.
- Buf *out_obj_path = buf_alloc();
- if ((err = get_tmp_filename(g, out_obj_path, final_o_basename))) {
- fprintf(stderr, "unable to create tmp dir: %s\n", err_str(err));
- exit(1);
- }
-
- Termination term;
- ZigList args = {};
- args.append(buf_ptr(self_exe_path));
- args.append("clang");
-
- if (c_file->preprocessor_only_basename == nullptr) {
- args.append("-c");
- }
-
- Buf *out_dep_path = g->disable_c_depfile ? nullptr : buf_sprintf("%s.d", buf_ptr(out_obj_path));
- const char *out_dep_path_cstr = (out_dep_path == nullptr) ? nullptr : buf_ptr(out_dep_path);
- FileExt ext = classify_file_ext(buf_ptr(c_source_basename), buf_len(c_source_basename));
- add_cc_args(g, args, out_dep_path_cstr, false, ext);
-
- args.append("-o");
- args.append(buf_ptr(out_obj_path));
-
- args.append(buf_ptr(c_source_file));
-
- for (size_t arg_i = 0; arg_i < c_file->args.length; arg_i += 1) {
- args.append(c_file->args.at(arg_i));
- }
-
- if (g->verbose_cc) {
- print_zig_cc_cmd(&args);
- }
- os_spawn_process(args, &term);
- if (term.how != TerminationIdClean || term.code != 0) {
- fprintf(stderr, "\nThe following command failed:\n");
- print_zig_cc_cmd(&args);
- exit(1);
- }
-
- if (out_dep_path != nullptr) {
- // add the files depended on to the cache system
- if ((err = cache_add_dep_file(cache_hash, out_dep_path, true))) {
- // Don't treat the absence of the .d file as a fatal error, the
- // compiler may not produce one eg. when compiling .s files
- if (err != ErrorFileNotFound) {
- fprintf(stderr, "Failed to add C source dependencies to cache: %s\n", err_str(err));
- exit(1);
- }
- }
- if (err != ErrorFileNotFound) {
- os_delete_file(out_dep_path);
- }
-
- if ((err = cache_final(cache_hash, &digest))) {
- fprintf(stderr, "Unable to finalize cache hash: %s\n", err_str(err));
- exit(1);
- }
- }
- artifact_dir = buf_alloc();
- os_path_join(o_dir, &digest, artifact_dir);
- if ((err = os_make_path(artifact_dir))) {
- fprintf(stderr, "Unable to create output directory '%s': %s",
- buf_ptr(artifact_dir), err_str(err));
- exit(1);
- }
- o_final_path = buf_alloc();
- os_path_join(artifact_dir, final_o_basename, o_final_path);
- if ((err = os_rename(out_obj_path, o_final_path))) {
- fprintf(stderr, "Unable to rename object: %s\n", err_str(err));
- exit(1);
- }
- } else {
- // cache hit
- artifact_dir = buf_alloc();
- os_path_join(o_dir, &digest, artifact_dir);
- o_final_path = buf_alloc();
- os_path_join(artifact_dir, final_o_basename, o_final_path);
- }
-
- g->c_artifact_dir = artifact_dir;
- g->link_objects.append(o_final_path);
- g->caches_to_release.append(cache_hash);
-
- stage2_progress_end(child_prog_node);
-}
-
-// returns true if we had any cache misses
-static void gen_c_objects(CodeGen *g) {
- Error err;
-
- if (g->c_source_files.length == 0)
- return;
-
- Buf *self_exe_path = buf_alloc();
- if ((err = os_self_exe_path(self_exe_path))) {
- fprintf(stderr, "Unable to get self exe path: %s\n", err_str(err));
- exit(1);
- }
-
- codegen_add_time_event(g, "Compile C Objects");
- const char *c_prog_name = "Compile C Objects";
- codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, c_prog_name, strlen(c_prog_name),
- g->c_source_files.length));
-
- for (size_t c_file_i = 0; c_file_i < g->c_source_files.length; c_file_i += 1) {
- CFile *c_file = g->c_source_files.at(c_file_i);
- gen_c_object(g, self_exe_path, c_file);
- }
-}
-
-void codegen_add_object(CodeGen *g, Buf *object_path) {
- g->link_objects.append(object_path);
-}
-
-// Must be coordinated with with CIntType enum
-static const char *c_int_type_names[] = {
- "short",
- "unsigned short",
- "int",
- "unsigned int",
- "long",
- "unsigned long",
- "long long",
- "unsigned long long",
-};
-
-struct GenH {
- ZigList types_to_declare;
-};
-
-static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, ZigType *type_entry) {
- if (type_entry->gen_h_loop_flag)
- return;
- type_entry->gen_h_loop_flag = true;
-
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- case ZigTypeIdMetaType:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdBoundFn:
- case ZigTypeIdErrorUnion:
- case ZigTypeIdErrorSet:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- zig_unreachable();
- case ZigTypeIdVoid:
- case ZigTypeIdUnreachable:
- return;
- case ZigTypeIdBool:
- g->c_want_stdbool = true;
- return;
- case ZigTypeIdInt:
- g->c_want_stdint = true;
- return;
- case ZigTypeIdFloat:
- return;
- case ZigTypeIdOpaque:
- gen_h->types_to_declare.append(type_entry);
- return;
- case ZigTypeIdStruct:
- if(type_entry->data.structure.layout == ContainerLayoutExtern) {
- for (uint32_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
- TypeStructField *field = type_entry->data.structure.fields[i];
- prepend_c_type_to_decl_list(g, gen_h, field->type_entry);
- }
- }
- gen_h->types_to_declare.append(type_entry);
- return;
- case ZigTypeIdUnion:
- for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) {
- TypeUnionField *field = &type_entry->data.unionation.fields[i];
- prepend_c_type_to_decl_list(g, gen_h, field->type_entry);
- }
- gen_h->types_to_declare.append(type_entry);
- return;
- case ZigTypeIdEnum:
- prepend_c_type_to_decl_list(g, gen_h, type_entry->data.enumeration.tag_int_type);
- gen_h->types_to_declare.append(type_entry);
- return;
- case ZigTypeIdPointer:
- prepend_c_type_to_decl_list(g, gen_h, type_entry->data.pointer.child_type);
- return;
- case ZigTypeIdArray:
- prepend_c_type_to_decl_list(g, gen_h, type_entry->data.array.child_type);
- return;
- case ZigTypeIdVector:
- prepend_c_type_to_decl_list(g, gen_h, type_entry->data.vector.elem_type);
- return;
- case ZigTypeIdOptional:
- prepend_c_type_to_decl_list(g, gen_h, type_entry->data.maybe.child_type);
- return;
- case ZigTypeIdFn:
- for (size_t i = 0; i < type_entry->data.fn.fn_type_id.param_count; i += 1) {
- prepend_c_type_to_decl_list(g, gen_h, type_entry->data.fn.fn_type_id.param_info[i].type);
- }
- prepend_c_type_to_decl_list(g, gen_h, type_entry->data.fn.fn_type_id.return_type);
- return;
- }
-}
-
-static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_buf) {
- assert(type_entry);
-
- for (size_t i = 0; i < array_length(c_int_type_names); i += 1) {
- if (type_entry == g->builtin_types.entry_c_int[i]) {
- buf_init_from_str(out_buf, c_int_type_names[i]);
- return;
- }
- }
- if (type_entry == g->builtin_types.entry_c_longdouble) {
- buf_init_from_str(out_buf, "long double");
- return;
- }
- if (type_entry == g->builtin_types.entry_c_void) {
- buf_init_from_str(out_buf, "void");
- return;
- }
- if (type_entry == g->builtin_types.entry_isize) {
- g->c_want_stdint = true;
- buf_init_from_str(out_buf, "intptr_t");
- return;
- }
- if (type_entry == g->builtin_types.entry_usize) {
- g->c_want_stdint = true;
- buf_init_from_str(out_buf, "uintptr_t");
- return;
- }
-
- prepend_c_type_to_decl_list(g, gen_h, type_entry);
-
- switch (type_entry->id) {
- case ZigTypeIdVoid:
- buf_init_from_str(out_buf, "void");
- break;
- case ZigTypeIdBool:
- buf_init_from_str(out_buf, "bool");
- break;
- case ZigTypeIdUnreachable:
- buf_init_from_str(out_buf, "__attribute__((__noreturn__)) void");
- break;
- case ZigTypeIdFloat:
- switch (type_entry->data.floating.bit_count) {
- case 32:
- buf_init_from_str(out_buf, "float");
- break;
- case 64:
- buf_init_from_str(out_buf, "double");
- break;
- case 80:
- buf_init_from_str(out_buf, "__float80");
- break;
- case 128:
- buf_init_from_str(out_buf, "__float128");
- break;
- default:
- zig_unreachable();
- }
- break;
- case ZigTypeIdInt:
- buf_resize(out_buf, 0);
- buf_appendf(out_buf, "%sint%" PRIu32 "_t",
- type_entry->data.integral.is_signed ? "" : "u",
- type_entry->data.integral.bit_count);
- break;
- case ZigTypeIdPointer:
- {
- Buf child_buf = BUF_INIT;
- ZigType *child_type = type_entry->data.pointer.child_type;
- get_c_type(g, gen_h, child_type, &child_buf);
-
- const char *const_str = type_entry->data.pointer.is_const ? "const " : "";
- buf_resize(out_buf, 0);
- buf_appendf(out_buf, "%s%s *", const_str, buf_ptr(&child_buf));
- break;
- }
- case ZigTypeIdOptional:
- {
- ZigType *child_type = type_entry->data.maybe.child_type;
- if (!type_has_bits(g, child_type)) {
- buf_init_from_str(out_buf, "bool");
- return;
- } else if (type_is_nonnull_ptr(g, child_type)) {
- return get_c_type(g, gen_h, child_type, out_buf);
- } else {
- zig_unreachable();
- }
- }
- case ZigTypeIdStruct:
- case ZigTypeIdOpaque:
- {
- buf_init_from_str(out_buf, "struct ");
- buf_append_buf(out_buf, type_h_name(type_entry));
- return;
- }
- case ZigTypeIdUnion:
- {
- buf_init_from_str(out_buf, "union ");
- buf_append_buf(out_buf, type_h_name(type_entry));
- return;
- }
- case ZigTypeIdEnum:
- {
- buf_init_from_str(out_buf, "enum ");
- buf_append_buf(out_buf, type_h_name(type_entry));
- return;
- }
- case ZigTypeIdArray:
- {
- ZigTypeArray *array_data = &type_entry->data.array;
-
- Buf *child_buf = buf_alloc();
- get_c_type(g, gen_h, array_data->child_type, child_buf);
-
- buf_resize(out_buf, 0);
- buf_appendf(out_buf, "%s", buf_ptr(child_buf));
- return;
- }
- case ZigTypeIdVector:
- zig_panic("TODO implement get_c_type for vector types");
- case ZigTypeIdErrorUnion:
- case ZigTypeIdErrorSet:
- case ZigTypeIdFn:
- zig_panic("TODO implement get_c_type for more types");
- case ZigTypeIdInvalid:
- case ZigTypeIdMetaType:
- case ZigTypeIdBoundFn:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- zig_unreachable();
- }
-}
-
-static const char *preprocessor_alphabet1 = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
-static const char *preprocessor_alphabet2 = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
-
-static bool need_to_preprocessor_mangle(Buf *src) {
- for (size_t i = 0; i < buf_len(src); i += 1) {
- const char *alphabet = (i == 0) ? preprocessor_alphabet1 : preprocessor_alphabet2;
- uint8_t byte = buf_ptr(src)[i];
- if (strchr(alphabet, byte) == nullptr) {
- return true;
- }
- }
- return false;
-}
-
-static Buf *preprocessor_mangle(Buf *src) {
- if (!need_to_preprocessor_mangle(src)) {
- return buf_create_from_buf(src);
- }
- Buf *result = buf_alloc();
- for (size_t i = 0; i < buf_len(src); i += 1) {
- const char *alphabet = (i == 0) ? preprocessor_alphabet1 : preprocessor_alphabet2;
- uint8_t byte = buf_ptr(src)[i];
- if (strchr(alphabet, byte) == nullptr) {
- // perform escape
- buf_appendf(result, "_%02x_", byte);
- } else {
- buf_append_char(result, byte);
- }
- }
- return result;
-}
-
-static void gen_h_file_types(CodeGen* g, GenH* gen_h, Buf* out_buf) {
- for (size_t type_i = 0; type_i < gen_h->types_to_declare.length; type_i += 1) {
- ZigType *type_entry = gen_h->types_to_declare.at(type_i);
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- case ZigTypeIdMetaType:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdUnreachable:
- case ZigTypeIdInt:
- case ZigTypeIdFloat:
- case ZigTypeIdPointer:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdArray:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdErrorUnion:
- case ZigTypeIdErrorSet:
- case ZigTypeIdBoundFn:
- case ZigTypeIdOptional:
- case ZigTypeIdFn:
- case ZigTypeIdVector:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- zig_unreachable();
-
- case ZigTypeIdEnum:
- if (type_entry->data.enumeration.layout == ContainerLayoutExtern) {
- buf_appendf(out_buf, "enum %s {\n", buf_ptr(type_h_name(type_entry)));
- for (uint32_t field_i = 0; field_i < type_entry->data.enumeration.src_field_count; field_i += 1) {
- TypeEnumField *enum_field = &type_entry->data.enumeration.fields[field_i];
- Buf *value_buf = buf_alloc();
- bigint_append_buf(value_buf, &enum_field->value, 10);
- buf_appendf(out_buf, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf));
- if (field_i != type_entry->data.enumeration.src_field_count - 1) {
- buf_appendf(out_buf, ",");
- }
- buf_appendf(out_buf, "\n");
- }
- buf_appendf(out_buf, "};\n\n");
- } else {
- buf_appendf(out_buf, "enum %s;\n\n", buf_ptr(type_h_name(type_entry)));
- }
- break;
- case ZigTypeIdStruct:
- if (type_entry->data.structure.layout == ContainerLayoutExtern) {
- buf_appendf(out_buf, "struct %s {\n", buf_ptr(type_h_name(type_entry)));
- for (uint32_t field_i = 0; field_i < type_entry->data.structure.src_field_count; field_i += 1) {
- TypeStructField *struct_field = type_entry->data.structure.fields[field_i];
-
- Buf *type_name_buf = buf_alloc();
- get_c_type(g, gen_h, struct_field->type_entry, type_name_buf);
-
- if (struct_field->type_entry->id == ZigTypeIdArray) {
- buf_appendf(out_buf, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf),
- buf_ptr(struct_field->name),
- struct_field->type_entry->data.array.len);
- } else {
- buf_appendf(out_buf, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name));
- }
-
- }
- buf_appendf(out_buf, "};\n\n");
- } else {
- buf_appendf(out_buf, "struct %s;\n\n", buf_ptr(type_h_name(type_entry)));
- }
- break;
- case ZigTypeIdUnion:
- if (type_entry->data.unionation.layout == ContainerLayoutExtern) {
- buf_appendf(out_buf, "union %s {\n", buf_ptr(type_h_name(type_entry)));
- for (uint32_t field_i = 0; field_i < type_entry->data.unionation.src_field_count; field_i += 1) {
- TypeUnionField *union_field = &type_entry->data.unionation.fields[field_i];
-
- Buf *type_name_buf = buf_alloc();
- get_c_type(g, gen_h, union_field->type_entry, type_name_buf);
- buf_appendf(out_buf, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name));
- }
- buf_appendf(out_buf, "};\n\n");
- } else {
- buf_appendf(out_buf, "union %s;\n\n", buf_ptr(type_h_name(type_entry)));
- }
- break;
- case ZigTypeIdOpaque:
- buf_appendf(out_buf, "struct %s;\n\n", buf_ptr(type_h_name(type_entry)));
- break;
- }
- }
-}
-
-static void gen_h_file_functions(CodeGen* g, GenH* gen_h, Buf* out_buf, Buf* export_macro) {
- for (size_t fn_def_i = 0; fn_def_i < g->fn_defs.length; fn_def_i += 1) {
- ZigFn *fn_table_entry = g->fn_defs.at(fn_def_i);
-
- if (fn_table_entry->export_list.length == 0)
- continue;
-
- FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id;
-
- Buf return_type_c = BUF_INIT;
- get_c_type(g, gen_h, fn_type_id->return_type, &return_type_c);
-
- Buf *symbol_name;
- if (fn_table_entry->export_list.length == 0) {
- symbol_name = &fn_table_entry->symbol_name;
- } else {
- GlobalExport *fn_export = &fn_table_entry->export_list.items[0];
- symbol_name = &fn_export->name;
- }
-
- if (export_macro != nullptr) {
- buf_appendf(out_buf, "%s %s %s(",
- buf_ptr(export_macro),
- buf_ptr(&return_type_c),
- buf_ptr(symbol_name));
- } else {
- buf_appendf(out_buf, "%s %s(",
- buf_ptr(&return_type_c),
- buf_ptr(symbol_name));
- }
-
- Buf param_type_c = BUF_INIT;
- if (fn_type_id->param_count > 0) {
- for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) {
- FnTypeParamInfo *param_info = &fn_type_id->param_info[param_i];
- AstNode *param_decl_node = get_param_decl_node(fn_table_entry, param_i);
- Buf *param_name = param_decl_node->data.param_decl.name;
-
- const char *comma_str = (param_i == 0) ? "" : ", ";
- const char *restrict_str = param_info->is_noalias ? "restrict" : "";
- get_c_type(g, gen_h, param_info->type, ¶m_type_c);
-
- if (param_info->type->id == ZigTypeIdArray) {
- // Arrays decay to pointers
- buf_appendf(out_buf, "%s%s%s %s[]", comma_str, buf_ptr(¶m_type_c),
- restrict_str, buf_ptr(param_name));
- } else {
- buf_appendf(out_buf, "%s%s%s %s", comma_str, buf_ptr(¶m_type_c),
- restrict_str, buf_ptr(param_name));
- }
- }
- buf_appendf(out_buf, ")");
- } else {
- buf_appendf(out_buf, "void)");
- }
-
- buf_appendf(out_buf, ";\n");
- }
-}
-
-static void gen_h_file_variables(CodeGen* g, GenH* gen_h, Buf* h_buf, Buf* export_macro) {
- for (size_t exp_var_i = 0; exp_var_i < g->global_vars.length; exp_var_i += 1) {
- ZigVar* var = g->global_vars.at(exp_var_i)->var;
- if (var->export_list.length == 0)
- continue;
-
- Buf var_type_c = BUF_INIT;
- get_c_type(g, gen_h, var->var_type, &var_type_c);
-
- if (export_macro != nullptr) {
- buf_appendf(h_buf, "extern %s %s %s;\n",
- buf_ptr(export_macro),
- buf_ptr(&var_type_c),
- var->name);
- } else {
- buf_appendf(h_buf, "extern %s %s;\n",
- buf_ptr(&var_type_c),
- var->name);
- }
- }
-}
-
-static void gen_h_file(CodeGen *g) {
- GenH gen_h_data = {0};
- GenH *gen_h = &gen_h_data;
-
- assert(!g->is_test_build);
- assert(!g->disable_gen_h);
-
- Buf *out_h_path = buf_sprintf("%s" OS_SEP "%s.h", buf_ptr(g->output_dir), buf_ptr(g->root_out_name));
-
- FILE *out_h = fopen(buf_ptr(out_h_path), "wb");
- if (!out_h)
- zig_panic("unable to open %s: %s\n", buf_ptr(out_h_path), strerror(errno));
-
- Buf *export_macro = nullptr;
- if (g->is_dynamic) {
- export_macro = preprocessor_mangle(buf_sprintf("%s_EXPORT", buf_ptr(g->root_out_name)));
- buf_upcase(export_macro);
- }
-
- Buf fns_buf = BUF_INIT;
- buf_resize(&fns_buf, 0);
- gen_h_file_functions(g, gen_h, &fns_buf, export_macro);
-
- Buf vars_buf = BUF_INIT;
- buf_resize(&vars_buf, 0);
- gen_h_file_variables(g, gen_h, &vars_buf, export_macro);
-
- // Types will be populated by exported functions and variables so it has to run last.
- Buf types_buf = BUF_INIT;
- buf_resize(&types_buf, 0);
- gen_h_file_types(g, gen_h, &types_buf);
-
- Buf *ifdef_dance_name = preprocessor_mangle(buf_sprintf("%s_H", buf_ptr(g->root_out_name)));
- buf_upcase(ifdef_dance_name);
-
- fprintf(out_h, "#ifndef %s\n", buf_ptr(ifdef_dance_name));
- fprintf(out_h, "#define %s\n\n", buf_ptr(ifdef_dance_name));
-
- if (g->c_want_stdbool)
- fprintf(out_h, "#include \n");
- if (g->c_want_stdint)
- fprintf(out_h, "#include \n");
-
- fprintf(out_h, "\n");
-
- if (g->is_dynamic) {
- fprintf(out_h, "#if defined(_WIN32)\n");
- fprintf(out_h, "#define %s __declspec(dllimport)\n", buf_ptr(export_macro));
- fprintf(out_h, "#else\n");
- fprintf(out_h, "#define %s __attribute__((visibility (\"default\")))\n",
- buf_ptr(export_macro));
- fprintf(out_h, "#endif\n");
- fprintf(out_h, "\n");
- }
-
- fprintf(out_h, "#ifdef __cplusplus\n");
- fprintf(out_h, "extern \"C\" {\n");
- fprintf(out_h, "#endif\n");
- fprintf(out_h, "\n");
-
- fprintf(out_h, "%s", buf_ptr(&types_buf));
- fprintf(out_h, "%s\n", buf_ptr(&fns_buf));
- fprintf(out_h, "%s\n", buf_ptr(&vars_buf));
-
- fprintf(out_h, "#ifdef __cplusplus\n");
- fprintf(out_h, "} // extern \"C\"\n");
- fprintf(out_h, "#endif\n\n");
-
- fprintf(out_h, "#endif // %s\n", buf_ptr(ifdef_dance_name));
-
- if (fclose(out_h))
- zig_panic("unable to close h file: %s", strerror(errno));
-}
-
void codegen_print_timing_report(CodeGen *g, FILE *f) {
double start_time = g->timing_events.at(0).time;
double end_time = g->timing_events.last().time;
@@ -10734,193 +9235,20 @@ void codegen_add_time_event(CodeGen *g, const char *name) {
g->timing_events.append({seconds, name});
}
-static void add_cache_pkg(CodeGen *g, CacheHash *ch, ZigPackage *pkg) {
- if (buf_len(&pkg->root_src_path) == 0)
- return;
- pkg->added_to_cache = true;
+void codegen_build_object(CodeGen *g) {
+ g->have_err_ret_tracing = detect_err_ret_tracing(g);
- Buf *rel_full_path = buf_alloc();
- os_path_join(&pkg->root_src_dir, &pkg->root_src_path, rel_full_path);
- cache_file(ch, rel_full_path);
+ init(g);
- auto it = pkg->package_table.entry_iterator();
- for (;;) {
- auto *entry = it.next();
- if (!entry)
- break;
+ codegen_add_time_event(g, "Semantic Analysis");
+ const char *progress_name = "Semantic Analysis";
+ codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node,
+ progress_name, strlen(progress_name), 0));
- if (!pkg->added_to_cache) {
- cache_buf(ch, entry->key);
- add_cache_pkg(g, ch, entry->value);
- }
- }
-}
+ gen_root_source(g);
-// Called before init()
-// is_cache_hit takes into account gen_c_objects
-static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
- Error err;
-
- Buf *compiler_id;
- if ((err = get_compiler_id(&compiler_id)))
- return err;
-
- CacheHash *ch = &g->cache_hash;
- cache_init(ch, manifest_dir);
-
- add_cache_pkg(g, ch, g->main_pkg);
- if (g->linker_script != nullptr) {
- cache_file(ch, buf_create_from_str(g->linker_script));
- }
- cache_buf(ch, compiler_id);
- cache_buf(ch, g->root_out_name);
- cache_buf(ch, g->zig_lib_dir);
- cache_buf(ch, g->zig_std_dir);
- cache_list_of_link_lib(ch, g->link_libs_list.items, g->link_libs_list.length);
- cache_list_of_buf(ch, g->darwin_frameworks.items, g->darwin_frameworks.length);
- cache_list_of_buf(ch, g->rpath_list.items, g->rpath_list.length);
- cache_list_of_buf(ch, g->forbidden_libs.items, g->forbidden_libs.length);
- cache_int(ch, g->build_mode);
- cache_int(ch, g->out_type);
- cache_bool(ch, g->zig_target->is_native_os);
- cache_bool(ch, g->zig_target->is_native_cpu);
- cache_int(ch, g->zig_target->arch);
- cache_int(ch, g->zig_target->vendor);
- cache_int(ch, g->zig_target->os);
- cache_int(ch, g->zig_target->abi);
- if (g->zig_target->cache_hash != nullptr) {
- cache_mem(ch, g->zig_target->cache_hash, g->zig_target->cache_hash_len);
- }
- if (g->zig_target->glibc_or_darwin_version != nullptr) {
- cache_int(ch, g->zig_target->glibc_or_darwin_version->major);
- cache_int(ch, g->zig_target->glibc_or_darwin_version->minor);
- cache_int(ch, g->zig_target->glibc_or_darwin_version->patch);
- }
- if (g->zig_target->dynamic_linker != nullptr) {
- cache_str(ch, g->zig_target->dynamic_linker);
- }
- cache_int(ch, detect_subsystem(g));
- cache_bool(ch, g->strip_debug_symbols);
- cache_bool(ch, g->is_test_build);
- if (g->is_test_build) {
- cache_buf_opt(ch, g->test_filter);
- cache_buf_opt(ch, g->test_name_prefix);
- cache_bool(ch, g->test_is_evented);
- }
- cache_bool(ch, g->link_eh_frame_hdr);
- cache_bool(ch, g->is_single_threaded);
- cache_bool(ch, g->linker_rdynamic);
- cache_bool(ch, g->each_lib_rpath);
- cache_bool(ch, g->disable_gen_h);
- cache_bool(ch, g->bundle_compiler_rt);
- cache_bool(ch, want_valgrind_support(g));
- cache_bool(ch, g->have_pic);
- cache_bool(ch, g->have_dynamic_link);
- cache_bool(ch, g->have_stack_probing);
- cache_bool(ch, g->have_sanitize_c);
- cache_bool(ch, g->is_dummy_so);
- cache_bool(ch, g->function_sections);
- cache_bool(ch, g->enable_dump_analysis);
- cache_bool(ch, g->enable_doc_generation);
- cache_bool(ch, g->emit_bin);
- cache_bool(ch, g->emit_llvm_ir);
- cache_bool(ch, g->emit_asm);
- cache_bool(ch, g->is_versioned);
- cache_usize(ch, g->version_major);
- cache_usize(ch, g->version_minor);
- cache_usize(ch, g->version_patch);
- cache_list_of_str(ch, g->llvm_argv, g->llvm_argv_len);
- cache_list_of_str(ch, g->clang_argv, g->clang_argv_len);
- cache_list_of_str(ch, g->lib_dirs.items, g->lib_dirs.length);
- cache_list_of_str(ch, g->framework_dirs.items, g->framework_dirs.length);
- if (g->libc) {
- cache_slice(ch, Slice{g->libc->include_dir, g->libc->include_dir_len});
- cache_slice(ch, Slice{g->libc->sys_include_dir, g->libc->sys_include_dir_len});
- cache_slice(ch, Slice{g->libc->crt_dir, g->libc->crt_dir_len});
- cache_slice(ch, Slice{g->libc->msvc_lib_dir, g->libc->msvc_lib_dir_len});
- cache_slice(ch, Slice{g->libc->kernel32_lib_dir, g->libc->kernel32_lib_dir_len});
- }
- cache_buf_opt(ch, g->version_script_path);
- cache_buf_opt(ch, g->override_soname);
- cache_buf_opt(ch, g->linker_optimization);
- cache_int(ch, g->linker_gc_sections);
- cache_int(ch, g->linker_allow_shlib_undefined);
- cache_int(ch, g->linker_bind_global_refs_locally);
- cache_bool(ch, g->linker_z_nodelete);
- cache_bool(ch, g->linker_z_defs);
- cache_usize(ch, g->stack_size_override);
-
- // gen_c_objects appends objects to g->link_objects which we want to include in the hash
- gen_c_objects(g);
- cache_list_of_file(ch, g->link_objects.items, g->link_objects.length);
-
- buf_resize(digest, 0);
- if ((err = cache_hit(ch, digest))) {
- if (err != ErrorInvalidFormat)
- return err;
- }
-
- if (ch->manifest_file_path != nullptr) {
- g->caches_to_release.append(ch);
- }
-
- return ErrorNone;
-}
-
-static void resolve_out_paths(CodeGen *g) {
- assert(g->output_dir != nullptr);
- assert(g->root_out_name != nullptr);
-
- if (g->emit_bin) {
- Buf *out_basename = buf_create_from_buf(g->root_out_name);
- Buf *o_basename = buf_create_from_buf(g->root_out_name);
- switch (g->out_type) {
- case OutTypeUnknown:
- zig_unreachable();
- case OutTypeObj:
- if (need_llvm_module(g) && g->link_objects.length != 0 && !g->enable_cache &&
- buf_eql_buf(o_basename, out_basename))
- {
- // make it not collide with main output object
- buf_append_str(o_basename, ".root");
- }
- buf_append_str(o_basename, target_o_file_ext(g->zig_target));
- buf_append_str(out_basename, target_o_file_ext(g->zig_target));
- break;
- case OutTypeExe:
- buf_append_str(o_basename, target_o_file_ext(g->zig_target));
- buf_append_str(out_basename, target_exe_file_ext(g->zig_target));
- break;
- case OutTypeLib:
- buf_append_str(o_basename, target_o_file_ext(g->zig_target));
- buf_resize(out_basename, 0);
- buf_append_str(out_basename, target_lib_file_prefix(g->zig_target));
- buf_append_buf(out_basename, g->root_out_name);
- buf_append_str(out_basename, target_lib_file_ext(g->zig_target, !g->is_dynamic, g->is_versioned,
- g->version_major, g->version_minor, g->version_patch));
- break;
- }
- os_path_join(g->output_dir, o_basename, &g->o_file_output_path);
- os_path_join(g->output_dir, out_basename, &g->bin_file_output_path);
- }
- if (g->emit_asm) {
- Buf *asm_basename = buf_create_from_buf(g->root_out_name);
- const char *asm_ext = target_asm_file_ext(g->zig_target);
- buf_append_str(asm_basename, asm_ext);
- os_path_join(g->output_dir, asm_basename, &g->asm_file_output_path);
- }
- if (g->emit_llvm_ir) {
- Buf *llvm_ir_basename = buf_create_from_buf(g->root_out_name);
- const char *llvm_ir_ext = target_llvm_ir_file_ext(g->zig_target);
- buf_append_str(llvm_ir_basename, llvm_ir_ext);
- os_path_join(g->output_dir, llvm_ir_basename, &g->llvm_ir_file_output_path);
- }
-}
-
-static void output_type_information(CodeGen *g) {
- if (g->enable_dump_analysis) {
- const char *analysis_json_filename = buf_ptr(buf_sprintf("%s" OS_SEP "%s-analysis.json",
- buf_ptr(g->output_dir), buf_ptr(g->root_out_name)));
+ if (buf_len(&g->analysis_json_output_path) != 0) {
+ const char *analysis_json_filename = buf_ptr(&g->analysis_json_output_path);
FILE *f = fopen(analysis_json_filename, "wb");
if (f == nullptr) {
fprintf(stderr, "Unable to open '%s': %s\n", analysis_json_filename, strerror(errno));
@@ -10932,9 +9260,9 @@ static void output_type_information(CodeGen *g) {
exit(1);
}
}
- if (g->enable_doc_generation) {
+ if (buf_len(&g->docs_output_path) != 0) {
Error err;
- Buf *doc_dir_path = buf_sprintf("%s" OS_SEP "docs", buf_ptr(g->output_dir));
+ Buf *doc_dir_path = &g->docs_output_path;
if ((err = os_make_path(doc_dir_path))) {
fprintf(stderr, "Unable to create directory %s: %s\n", buf_ptr(doc_dir_path), err_str(err));
exit(1);
@@ -10970,146 +9298,27 @@ static void output_type_information(CodeGen *g) {
exit(1);
}
}
-}
-static void init_output_dir(CodeGen *g, Buf *digest) {
- if (main_output_dir_is_just_one_c_object_post(g)) {
- g->output_dir = buf_alloc();
- os_path_dirname(g->link_objects.at(0), g->output_dir);
- } else {
- g->output_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s",
- buf_ptr(g->cache_dir), buf_ptr(digest));
- }
-}
-
-void codegen_build_and_link(CodeGen *g) {
- Error err;
- assert(g->out_type != OutTypeUnknown);
-
- if (!g->enable_cache) {
- if (g->output_dir == nullptr) {
- g->output_dir = buf_create_from_str(".");
- } else if ((err = os_make_path(g->output_dir))) {
- fprintf(stderr, "Unable to create output directory: %s\n", err_str(err));
- exit(1);
- }
+ codegen_add_time_event(g, "Code Generation");
+ {
+ const char *progress_name = "Code Generation";
+ codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node,
+ progress_name, strlen(progress_name), 0));
}
- g->have_dynamic_link = detect_dynamic_link(g);
- g->have_pic = detect_pic(g);
- g->is_single_threaded = detect_single_threaded(g);
- g->have_err_ret_tracing = detect_err_ret_tracing(g);
- g->have_sanitize_c = detect_sanitize_c(g);
- detect_libc(g);
-
- Buf digest = BUF_INIT;
- if (g->enable_cache) {
- Buf *manifest_dir = buf_alloc();
- os_path_join(g->cache_dir, buf_create_from_str(CACHE_HASH_SUBDIR), manifest_dir);
-
- if ((err = check_cache(g, manifest_dir, &digest))) {
- if (err == ErrorCacheUnavailable) {
- // message already printed
- } else if (err == ErrorNotDir) {
- fprintf(stderr, "Unable to check cache: %s is not a directory\n",
- buf_ptr(manifest_dir));
- } else {
- fprintf(stderr, "Unable to check cache: %s: %s\n", buf_ptr(manifest_dir), err_str(err));
- }
- exit(1);
- }
- } else {
- // There is a call to this in check_cache
- gen_c_objects(g);
+ do_code_gen(g);
+ codegen_add_time_event(g, "LLVM Emit Output");
+ {
+ const char *progress_name = "LLVM Emit Output";
+ codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node,
+ progress_name, strlen(progress_name), 0));
}
+ zig_llvm_emit_output(g);
- if (g->enable_cache && buf_len(&digest) != 0) {
- init_output_dir(g, &digest);
- resolve_out_paths(g);
- } else {
- if (need_llvm_module(g)) {
- init(g);
-
- codegen_add_time_event(g, "Semantic Analysis");
- const char *progress_name = "Semantic Analysis";
- codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node,
- progress_name, strlen(progress_name), 0));
-
- gen_root_source(g);
-
- }
- if (g->enable_cache) {
- if (buf_len(&digest) == 0) {
- if ((err = cache_final(&g->cache_hash, &digest))) {
- fprintf(stderr, "Unable to finalize cache hash: %s\n", err_str(err));
- exit(1);
- }
- }
- init_output_dir(g, &digest);
-
- if ((err = os_make_path(g->output_dir))) {
- fprintf(stderr, "Unable to create output directory: %s\n", err_str(err));
- exit(1);
- }
- }
- resolve_out_paths(g);
-
- if (g->enable_dump_analysis || g->enable_doc_generation) {
- output_type_information(g);
- }
-
- if (need_llvm_module(g)) {
- codegen_add_time_event(g, "Code Generation");
- {
- const char *progress_name = "Code Generation";
- codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node,
- progress_name, strlen(progress_name), 0));
- }
-
- do_code_gen(g);
- codegen_add_time_event(g, "LLVM Emit Output");
- {
- const char *progress_name = "LLVM Emit Output";
- codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node,
- progress_name, strlen(progress_name), 0));
- }
- zig_llvm_emit_output(g);
-
- if (!g->disable_gen_h && (g->out_type == OutTypeObj || g->out_type == OutTypeLib)) {
- codegen_add_time_event(g, "Generate .h");
- {
- const char *progress_name = "Generate .h";
- codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node,
- progress_name, strlen(progress_name), 0));
- }
- gen_h_file(g);
- }
- }
-
- // If we're outputting assembly or llvm IR we skip linking.
- // If we're making a library or executable we must link.
- // If there is more than one object, we have to link them (with -r).
- // Finally, if we didn't make an object from zig source, and we don't have caching enabled,
- // then we have an object from C source that we must copy to the output dir which we do with a -r link.
- if (g->emit_bin &&
- (g->out_type != OutTypeObj || g->link_objects.length > 1 ||
- (!need_llvm_module(g) && !g->enable_cache)))
- {
- codegen_link(g);
- }
- }
-
- codegen_release_caches(g);
codegen_add_time_event(g, "Done");
codegen_switch_sub_prog_node(g, nullptr);
}
-void codegen_release_caches(CodeGen *g) {
- while (g->caches_to_release.length != 0) {
- cache_release(g->caches_to_release.pop());
- }
-}
-
ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path,
const char *pkg_path)
{
@@ -11126,75 +9335,31 @@ ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const c
return pkg;
}
-CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType out_type,
- Stage2LibCInstallation *libc, const char *name, Stage2ProgressNode *parent_progress_node)
-{
- Stage2ProgressNode *child_progress_node = stage2_progress_start(
- parent_progress_node ? parent_progress_node : parent_gen->sub_progress_node,
- name, strlen(name), 0);
-
- CodeGen *child_gen = codegen_create(nullptr, root_src_path, parent_gen->zig_target, out_type,
- parent_gen->build_mode, parent_gen->zig_lib_dir, libc, get_global_cache_dir(), false, child_progress_node);
- child_gen->root_out_name = buf_create_from_str(name);
- child_gen->disable_gen_h = true;
- child_gen->want_stack_check = WantStackCheckDisabled;
- child_gen->want_sanitize_c = WantCSanitizeDisabled;
- child_gen->verbose_tokenize = parent_gen->verbose_tokenize;
- child_gen->verbose_ast = parent_gen->verbose_ast;
- child_gen->verbose_link = parent_gen->verbose_link;
- child_gen->verbose_ir = parent_gen->verbose_ir;
- child_gen->verbose_llvm_ir = parent_gen->verbose_llvm_ir;
- child_gen->verbose_cimport = parent_gen->verbose_cimport;
- child_gen->verbose_cc = parent_gen->verbose_cc;
- child_gen->verbose_llvm_cpu_features = parent_gen->verbose_llvm_cpu_features;
- child_gen->llvm_argv = parent_gen->llvm_argv;
-
- codegen_set_strip(child_gen, parent_gen->strip_debug_symbols);
- child_gen->want_pic = parent_gen->have_pic ? WantPICEnabled : WantPICDisabled;
- child_gen->valgrind_support = ValgrindSupportDisabled;
-
- codegen_set_errmsg_color(child_gen, parent_gen->err_color);
-
- child_gen->enable_cache = true;
-
- return child_gen;
+void codegen_destroy(CodeGen *g) {
+ if (g->pass1_arena != nullptr) {
+ g->pass1_arena->destruct(&heap::c_allocator);
+ g->pass1_arena = nullptr;
+ }
+ heap::c_allocator.destroy(g);
}
CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target,
- OutType out_type, BuildMode build_mode, Buf *override_lib_dir,
- Stage2LibCInstallation *libc, Buf *cache_dir, bool is_test_build, Stage2ProgressNode *progress_node)
+ BuildMode build_mode, Buf *override_lib_dir,
+ bool is_test_build)
{
CodeGen *g = heap::c_allocator.create();
- g->emit_bin = true;
g->pass1_arena = heap::ArenaAllocator::construct(&heap::c_allocator, &heap::c_allocator, "pass1");
- g->main_progress_node = progress_node;
-
- codegen_add_time_event(g, "Initialize");
- {
- const char *progress_name = "Initialize";
- codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node,
- progress_name, strlen(progress_name), 0));
- }
g->subsystem = TargetSubsystemAuto;
- g->libc = libc;
g->zig_target = target;
- g->cache_dir = cache_dir;
- if (override_lib_dir == nullptr) {
- g->zig_lib_dir = get_zig_lib_dir();
- } else {
- g->zig_lib_dir = override_lib_dir;
- }
+ assert(override_lib_dir != nullptr);
+ g->zig_lib_dir = override_lib_dir;
g->zig_std_dir = buf_alloc();
os_path_join(g->zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir);
- g->zig_c_headers_dir = buf_alloc();
- os_path_join(g->zig_lib_dir, buf_create_from_str("include"), g->zig_c_headers_dir);
-
g->build_mode = build_mode;
- g->out_type = out_type;
g->import_table.init(32);
g->builtin_fn_table.init(32);
g->primitive_type_table.init(32);
@@ -11237,7 +9402,7 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget
Buf resolved_main_pkg_path = os_path_resolve(&main_pkg_path, 1);
if (!buf_starts_with_buf(&resolved_root_src_path, &resolved_main_pkg_path)) {
- fprintf(stderr, "Root source path '%s' outside main package path '%s'",
+ fprintf(stderr, "Root source path '%s' outside main package path '%s'\n",
buf_ptr(root_src_path), buf_ptr(main_pkg_path));
exit(1);
}
@@ -11257,18 +9422,6 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget
g->zig_std_special_dir = buf_alloc();
os_path_join(g->zig_std_dir, buf_sprintf("special"), g->zig_std_special_dir);
- assert(target != nullptr);
- if (!target->is_native_os) {
- g->each_lib_rpath = false;
- } else {
- g->each_lib_rpath = true;
- }
-
- if (target_os_requires_libc(g->zig_target->os)) {
- g->libc_link_lib = create_link_lib(buf_create_from_str("c"));
- g->link_libs_list.append(g->libc_link_lib);
- }
-
target_triple_llvm(&g->llvm_triple_str, g->zig_target);
g->pointer_size_bytes = target_arch_pointer_bit_width(g->zig_target->arch) / 8;
@@ -11303,36 +9456,21 @@ void codegen_switch_sub_prog_node(CodeGen *g, Stage2ProgressNode *node) {
}
ZigValue *CodeGen::Intern::for_undefined() {
-#ifdef ZIG_ENABLE_MEM_PROFILE
- mem::intern_counters.x_undefined += 1;
-#endif
return &this->x_undefined;
}
ZigValue *CodeGen::Intern::for_void() {
-#ifdef ZIG_ENABLE_MEM_PROFILE
- mem::intern_counters.x_void += 1;
-#endif
return &this->x_void;
}
ZigValue *CodeGen::Intern::for_null() {
-#ifdef ZIG_ENABLE_MEM_PROFILE
- mem::intern_counters.x_null += 1;
-#endif
return &this->x_null;
}
ZigValue *CodeGen::Intern::for_unreachable() {
-#ifdef ZIG_ENABLE_MEM_PROFILE
- mem::intern_counters.x_unreachable += 1;
-#endif
return &this->x_unreachable;
}
ZigValue *CodeGen::Intern::for_zero_byte() {
-#ifdef ZIG_ENABLE_MEM_PROFILE
- mem::intern_counters.zero_byte += 1;
-#endif
return &this->zero_byte;
}
diff --git a/src/stage1/codegen.hpp b/src/stage1/codegen.hpp
new file mode 100644
index 0000000000..33b2f74757
--- /dev/null
+++ b/src/stage1/codegen.hpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#ifndef ZIG_CODEGEN_HPP
+#define ZIG_CODEGEN_HPP
+
+#include "parser.hpp"
+#include "errmsg.hpp"
+#include "target.hpp"
+#include "stage2.h"
+
+#include
+
+CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target,
+ BuildMode build_mode, Buf *zig_lib_dir, bool is_test_build);
+
+void codegen_build_object(CodeGen *g);
+void codegen_destroy(CodeGen *);
+
+void codegen_add_time_event(CodeGen *g, const char *name);
+void codegen_print_timing_report(CodeGen *g, FILE *f);
+
+ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path,
+ const char *pkg_path);
+
+TargetSubsystem detect_subsystem(CodeGen *g);
+
+bool codegen_fn_has_err_ret_tracing_arg(CodeGen *g, ZigType *return_type);
+bool codegen_fn_has_err_ret_tracing_stack(CodeGen *g, ZigFn *fn, bool is_async);
+
+ATTRIBUTE_NORETURN
+void codegen_report_errors_and_exit(CodeGen *g);
+
+void codegen_switch_sub_prog_node(CodeGen *g, Stage2ProgressNode *node);
+
+#endif
diff --git a/src/config.h.in b/src/stage1/config.h.in
similarity index 95%
rename from src/config.h.in
rename to src/stage1/config.h.in
index 2ec6c25b38..8c147e7d65 100644
--- a/src/config.h.in
+++ b/src/stage1/config.h.in
@@ -22,6 +22,4 @@
#define ZIG_LLVM_CONFIG_EXE "@LLVM_CONFIG_EXE@"
#define ZIG_DIA_GUIDS_LIB "@ZIG_DIA_GUIDS_LIB_ESCAPED@"
-#cmakedefine ZIG_ENABLE_MEM_PROFILE
-
#endif
diff --git a/src/dump_analysis.cpp b/src/stage1/dump_analysis.cpp
similarity index 99%
rename from src/dump_analysis.cpp
rename to src/stage1/dump_analysis.cpp
index 2f41341b14..df0d6f3ca2 100644
--- a/src/dump_analysis.cpp
+++ b/src/stage1/dump_analysis.cpp
@@ -6,11 +6,11 @@
*/
#include "dump_analysis.hpp"
-#include "compiler.hpp"
#include "analyze.hpp"
#include "config.h"
#include "ir.hpp"
#include "codegen.hpp"
+#include "os.hpp"
enum JsonWriterState {
JsonWriterStateInvalid,
@@ -1173,7 +1173,6 @@ static void anal_dump_fn(AnalDumpCtx *ctx, ZigFn *fn) {
}
void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const char *nl) {
- Error err;
AnalDumpCtx ctx = {};
ctx.g = g;
JsonWriter *jw = &ctx.jw;
@@ -1199,15 +1198,6 @@ void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const
jw_object_field(jw, "params");
jw_begin_object(jw);
{
- jw_object_field(jw, "zigId");
-
- Buf *compiler_id;
- if ((err = get_compiler_id(&compiler_id))) {
- fprintf(stderr, "Unable to determine compiler id: %s\n", err_str(err));
- exit(1);
- }
- jw_string(jw, buf_ptr(compiler_id));
-
jw_object_field(jw, "zigVersion");
jw_string(jw, ZIG_VERSION_STRING);
diff --git a/src/dump_analysis.hpp b/src/stage1/dump_analysis.hpp
similarity index 100%
rename from src/dump_analysis.hpp
rename to src/stage1/dump_analysis.hpp
diff --git a/src/stage1/empty.cpp b/src/stage1/empty.cpp
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/errmsg.cpp b/src/stage1/errmsg.cpp
similarity index 100%
rename from src/errmsg.cpp
rename to src/stage1/errmsg.cpp
diff --git a/src/errmsg.hpp b/src/stage1/errmsg.hpp
similarity index 91%
rename from src/errmsg.hpp
rename to src/stage1/errmsg.hpp
index e8b2f5872d..73cbd4e0d9 100644
--- a/src/errmsg.hpp
+++ b/src/stage1/errmsg.hpp
@@ -10,12 +10,7 @@
#include "buffer.hpp"
#include "list.hpp"
-
-enum ErrColor {
- ErrColorAuto,
- ErrColorOff,
- ErrColorOn,
-};
+#include "stage1.h"
struct ErrorMsg {
size_t line_start;
diff --git a/src/error.cpp b/src/stage1/error.cpp
similarity index 100%
rename from src/error.cpp
rename to src/stage1/error.cpp
diff --git a/src/error.hpp b/src/stage1/error.hpp
similarity index 100%
rename from src/error.hpp
rename to src/stage1/error.hpp
diff --git a/src/hash_map.hpp b/src/stage1/hash_map.hpp
similarity index 100%
rename from src/hash_map.hpp
rename to src/stage1/hash_map.hpp
diff --git a/src/heap.cpp b/src/stage1/heap.cpp
similarity index 85%
rename from src/heap.cpp
rename to src/stage1/heap.cpp
index 79c44d13dc..7e7a171bde 100644
--- a/src/heap.cpp
+++ b/src/stage1/heap.cpp
@@ -10,7 +10,6 @@
#include "config.h"
#include "heap.hpp"
-#include "mem_profile.hpp"
namespace heap {
@@ -48,21 +47,9 @@ void BootstrapAllocator::internal_deallocate(const mem::TypeInfo &info, void *pt
mem::os::free(ptr);
}
-void CAllocator::init(const char *name) {
-#ifdef ZIG_ENABLE_MEM_PROFILE
- this->profile = bootstrap_allocator.create();
- this->profile->init(name, "CAllocator");
-#endif
-}
+void CAllocator::init(const char *name) { }
-void CAllocator::deinit() {
-#ifdef ZIG_ENABLE_MEM_PROFILE
- assert(this->profile);
- this->profile->deinit();
- bootstrap_allocator.destroy(this->profile);
- this->profile = nullptr;
-#endif
-}
+void CAllocator::deinit() { }
CAllocator *CAllocator::construct(mem::Allocator *allocator, const char *name) {
auto p = new(allocator->create()) CAllocator();
@@ -75,23 +62,11 @@ void CAllocator::destruct(mem::Allocator *allocator) {
allocator->destroy(this);
}
-#ifdef ZIG_ENABLE_MEM_PROFILE
-void CAllocator::print_report(FILE *file) {
- this->profile->print_report(file);
-}
-#endif
-
void *CAllocator::internal_allocate(const mem::TypeInfo &info, size_t count) {
-#ifdef ZIG_ENABLE_MEM_PROFILE
- this->profile->record_alloc(info, count);
-#endif
return mem::os::calloc(count, info.size);
}
void *CAllocator::internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) {
-#ifdef ZIG_ENABLE_MEM_PROFILE
- this->profile->record_alloc(info, count);
-#endif
return mem::os::malloc(count * info.size);
}
@@ -103,17 +78,10 @@ void *CAllocator::internal_reallocate(const mem::TypeInfo &info, void *old_ptr,
}
void *CAllocator::internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) {
-#ifdef ZIG_ENABLE_MEM_PROFILE
- this->profile->record_dealloc(info, old_count);
- this->profile->record_alloc(info, new_count);
-#endif
return mem::os::realloc(old_ptr, new_count * info.size);
}
void CAllocator::internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) {
-#ifdef ZIG_ENABLE_MEM_PROFILE
- this->profile->record_dealloc(info, count);
-#endif
mem::os::free(ptr);
}
@@ -249,10 +217,6 @@ void ArenaAllocator::Impl::track_object(Object object) {
}
void ArenaAllocator::init(Allocator *backing, const char *name) {
-#ifdef ZIG_ENABLE_MEM_PROFILE
- this->profile = bootstrap_allocator.create();
- this->profile->init(name, "ArenaAllocator");
-#endif
this->impl = bootstrap_allocator.create();
{
auto &r = *this->impl;
@@ -309,13 +273,6 @@ void ArenaAllocator::deinit() {
t = prev;
}
}
-
-#ifdef ZIG_ENABLE_MEM_PROFILE
- assert(this->profile);
- this->profile->deinit();
- bootstrap_allocator.destroy(this->profile);
- this->profile = nullptr;
-#endif
}
ArenaAllocator *ArenaAllocator::construct(mem::Allocator *allocator, mem::Allocator *backing, const char *name) {
@@ -329,23 +286,11 @@ void ArenaAllocator::destruct(mem::Allocator *allocator) {
allocator->destroy(this);
}
-#ifdef ZIG_ENABLE_MEM_PROFILE
-void ArenaAllocator::print_report(FILE *file) {
- this->profile->print_report(file);
-}
-#endif
-
void *ArenaAllocator::internal_allocate(const mem::TypeInfo &info, size_t count) {
-#ifdef ZIG_ENABLE_MEM_PROFILE
- this->profile->record_alloc(info, count);
-#endif
return this->impl->allocate(info, count);
}
void *ArenaAllocator::internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) {
-#ifdef ZIG_ENABLE_MEM_PROFILE
- this->profile->record_alloc(info, count);
-#endif
return this->impl->allocate(info, count);
}
@@ -354,17 +299,10 @@ void *ArenaAllocator::internal_reallocate(const mem::TypeInfo &info, void *old_p
}
void *ArenaAllocator::internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) {
-#ifdef ZIG_ENABLE_MEM_PROFILE
- this->profile->record_dealloc(info, old_count);
- this->profile->record_alloc(info, new_count);
-#endif
return this->impl->reallocate(info, old_ptr, old_count, new_count);
}
void ArenaAllocator::internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) {
-#ifdef ZIG_ENABLE_MEM_PROFILE
- this->profile->record_dealloc(info, count);
-#endif
// noop
}
diff --git a/src/heap.hpp b/src/stage1/heap.hpp
similarity index 89%
rename from src/heap.hpp
rename to src/stage1/heap.hpp
index e22ec42967..ec5c81026d 100644
--- a/src/heap.hpp
+++ b/src/stage1/heap.hpp
@@ -12,12 +12,6 @@
#include "util_base.hpp"
#include "mem.hpp"
-#ifdef ZIG_ENABLE_MEM_PROFILE
-namespace mem {
- struct Profile;
-}
-#endif
-
namespace heap {
struct BootstrapAllocator final : mem::Allocator {
@@ -40,9 +34,6 @@ struct CAllocator final : mem::Allocator {
static CAllocator *construct(mem::Allocator *allocator, const char *name);
void destruct(mem::Allocator *allocator) final;
-#ifdef ZIG_ENABLE_MEM_PROFILE
- void print_report(FILE *file = nullptr);
-#endif
private:
ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate(const mem::TypeInfo &info, size_t count) final;
@@ -51,9 +42,6 @@ private:
void *internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final;
void internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) final;
-#ifdef ZIG_ENABLE_MEM_PROFILE
- mem::Profile *profile;
-#endif
};
//
@@ -71,9 +59,6 @@ struct ArenaAllocator final : mem::Allocator {
static ArenaAllocator *construct(mem::Allocator *allocator, mem::Allocator *backing, const char *name);
void destruct(mem::Allocator *allocator) final;
-#ifdef ZIG_ENABLE_MEM_PROFILE
- void print_report(FILE *file = nullptr);
-#endif
private:
ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate(const mem::TypeInfo &info, size_t count) final;
@@ -82,10 +67,6 @@ private:
void *internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final;
void internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) final;
-#ifdef ZIG_ENABLE_MEM_PROFILE
- mem::Profile *profile;
-#endif
-
struct Impl;
Impl *impl;
};
diff --git a/src/ir.cpp b/src/stage1/ir.cpp
similarity index 99%
rename from src/ir.cpp
rename to src/stage1/ir.cpp
index 804dee4181..9587200023 100644
--- a/src/ir.cpp
+++ b/src/stage1/ir.cpp
@@ -16716,16 +16716,12 @@ static IrInstGen *ir_analyze_bin_op_cmp_numeric(IrAnalyze *ira, IrInst *source_i
}
ZigType *dest_float_type = nullptr;
uint32_t op1_bits;
- if (instr_is_comptime(op1)) {
+ if (instr_is_comptime(op1) && result_type->id != ZigTypeIdVector) {
ZigValue *op1_val = ir_resolve_const(ira, op1, UndefOk);
if (op1_val == nullptr)
return ira->codegen->invalid_inst_gen;
if (op1_val->special == ConstValSpecialUndef)
return ir_const_undef(ira, source_instr, ira->codegen->builtin_types.entry_bool);
- if (result_type->id == ZigTypeIdVector) {
- ir_add_error(ira, &op1->base, buf_sprintf("compiler bug: TODO: support comptime vector here"));
- return ira->codegen->invalid_inst_gen;
- }
bool is_unsigned;
if (op1_is_float) {
BigInt bigint = {};
@@ -16751,6 +16747,7 @@ static IrInstGen *ir_analyze_bin_op_cmp_numeric(IrAnalyze *ira, IrInst *source_i
op1_bits += 1;
}
} else if (op1_is_float) {
+ ir_assert(op1_scalar_type->id == ZigTypeIdFloat, source_instr);
dest_float_type = op1_scalar_type;
} else {
ir_assert(op1_scalar_type->id == ZigTypeIdInt, source_instr);
@@ -16760,16 +16757,12 @@ static IrInstGen *ir_analyze_bin_op_cmp_numeric(IrAnalyze *ira, IrInst *source_i
}
}
uint32_t op2_bits;
- if (instr_is_comptime(op2)) {
+ if (instr_is_comptime(op2) && result_type->id != ZigTypeIdVector) {
ZigValue *op2_val = ir_resolve_const(ira, op2, UndefOk);
if (op2_val == nullptr)
return ira->codegen->invalid_inst_gen;
if (op2_val->special == ConstValSpecialUndef)
return ir_const_undef(ira, source_instr, ira->codegen->builtin_types.entry_bool);
- if (result_type->id == ZigTypeIdVector) {
- ir_add_error(ira, &op2->base, buf_sprintf("compiler bug: TODO: support comptime vector here"));
- return ira->codegen->invalid_inst_gen;
- }
bool is_unsigned;
if (op2_is_float) {
BigInt bigint = {};
@@ -16795,6 +16788,7 @@ static IrInstGen *ir_analyze_bin_op_cmp_numeric(IrAnalyze *ira, IrInst *source_i
op2_bits += 1;
}
} else if (op2_is_float) {
+ ir_assert(op2_scalar_type->id == ZigTypeIdFloat, source_instr);
dest_float_type = op2_scalar_type;
} else {
ir_assert(op2_scalar_type->id == ZigTypeIdInt, source_instr);
@@ -21935,7 +21929,17 @@ static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemP
return ira->codegen->invalid_inst_gen;
}
safety_check_on = false;
+ } else if (array_type->id == ZigTypeIdVector) {
+ uint64_t vector_len = array_type->data.vector.len;
+ if (index >= vector_len) {
+ ir_add_error_node(ira, elem_ptr_instruction->base.base.source_node,
+ buf_sprintf("index %" ZIG_PRI_u64 " outside vector of size %" ZIG_PRI_u64,
+ index, vector_len));
+ return ira->codegen->invalid_inst_gen;
+ }
+ safety_check_on = false;
}
+
if (array_type->id == ZigTypeIdVector) {
ZigType *elem_type = array_type->data.vector.elem_type;
uint32_t host_vec_len = array_type->data.vector.len;
@@ -22593,38 +22597,12 @@ static IrInstGen *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name
}
static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name, AstNode *source_node) {
- bool is_libc = target_is_libc_lib_name(ira->codegen->zig_target, buf_ptr(lib_name));
- if (is_libc && ira->codegen->libc_link_lib == nullptr && !ira->codegen->reported_bad_link_libc_error) {
- ir_add_error_node(ira, source_node,
- buf_sprintf("dependency on library c must be explicitly specified in the build command"));
+ const char *msg = stage2_add_link_lib(&ira->codegen->stage1, buf_ptr(lib_name), buf_len(lib_name),
+ buf_ptr(symbol_name), buf_len(symbol_name));
+ if (msg != nullptr) {
+ ir_add_error_node(ira, source_node, buf_create_from_str(msg));
ira->codegen->reported_bad_link_libc_error = true;
}
-
- LinkLib *link_lib = add_link_lib(ira->codegen, lib_name);
- for (size_t i = 0; i < link_lib->symbols.length; i += 1) {
- Buf *existing_symbol_name = link_lib->symbols.at(i);
- if (buf_eql_buf(existing_symbol_name, symbol_name)) {
- return;
- }
- }
-
- if (!is_libc && !target_is_wasm(ira->codegen->zig_target) && !ira->codegen->have_pic && !ira->codegen->reported_bad_link_libc_error) {
- ErrorMsg *msg = ir_add_error_node(ira, source_node,
- buf_sprintf("dependency on dynamic library '%s' requires enabling Position Independent Code",
- buf_ptr(lib_name)));
- add_error_note(ira->codegen, msg, source_node,
- buf_sprintf("fixed by `--library %s` or `-fPIC`", buf_ptr(lib_name)));
- ira->codegen->reported_bad_link_libc_error = true;
- }
-
- for (size_t i = 0; i < ira->codegen->forbidden_libs.length; i += 1) {
- Buf *forbidden_lib_name = ira->codegen->forbidden_libs.at(i);
- if (buf_eql_buf(lib_name, forbidden_lib_name)) {
- ir_add_error_node(ira, source_node,
- buf_sprintf("linking against forbidden library '%s'", buf_ptr(symbol_name)));
- }
- }
- link_lib->symbols.append(symbol_name);
}
static IrInstGen *ir_error_dependency_loop(IrAnalyze *ira, IrInst* source_instr) {
@@ -26378,13 +26356,6 @@ static IrInstGen *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstSrcType
return result;
}
-static void ir_cimport_cache_paths(Buf *cache_dir, Buf *tmp_c_file_digest, Buf *out_zig_dir, Buf *out_zig_path) {
- buf_resize(out_zig_dir, 0);
- buf_resize(out_zig_path, 0);
- buf_appendf(out_zig_dir, "%s" OS_SEP "o" OS_SEP "%s",
- buf_ptr(cache_dir), buf_ptr(tmp_c_file_digest));
- buf_appendf(out_zig_path, "%s" OS_SEP "cimport.zig", buf_ptr(out_zig_dir));
-}
static IrInstGen *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstSrcCImport *instruction) {
Error err;
AstNode *node = instruction->base.base.source_node;
@@ -26416,145 +26387,41 @@ static IrInstGen *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstSrcCImpo
cimport_pkg->package_table.put(buf_create_from_str("std"), ira->codegen->std_package);
buf_init_from_buf(&cimport_pkg->pkg_path, namespace_name);
- CacheHash *cache_hash;
- if ((err = create_c_object_cache(ira->codegen, &cache_hash, false))) {
- ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to create cache: %s", err_str(err)));
+ const char *out_zig_path_ptr;
+ size_t out_zig_path_len;
+ Stage2ErrorMsg *errors_ptr;
+ size_t errors_len;
+ if ((err = stage2_cimport(&ira->codegen->stage1,
+ buf_ptr(&cimport_scope->buf), buf_len(&cimport_scope->buf),
+ &out_zig_path_ptr, &out_zig_path_len,
+ &errors_ptr, &errors_len)))
+ {
+ if (err != ErrorCCompileErrors) {
+ ir_add_error_node(ira, node, buf_sprintf("C import failed: %s", err_str(err)));
+ return ira->codegen->invalid_inst_gen;
+ }
+
+ ErrorMsg *parent_err_msg = ir_add_error_node(ira, node, buf_sprintf("C import failed"));
+ if (!ira->codegen->stage1.link_libc) {
+ add_error_note(ira->codegen, parent_err_msg, node,
+ buf_sprintf("libc headers not available; compilation does not link against libc"));
+ }
+ for (size_t i = 0; i < errors_len; i += 1) {
+ Stage2ErrorMsg *clang_err = &errors_ptr[i];
+ // Clang can emit "too many errors, stopping now", in which case `source` and `filename_ptr` are null
+ if (clang_err->source && clang_err->filename_ptr) {
+ ErrorMsg *err_msg = err_msg_create_with_offset(
+ clang_err->filename_ptr ?
+ buf_create_from_mem(clang_err->filename_ptr, clang_err->filename_len) : buf_alloc(),
+ clang_err->line, clang_err->column, clang_err->offset, clang_err->source,
+ buf_create_from_mem(clang_err->msg_ptr, clang_err->msg_len));
+ err_msg_add_note(parent_err_msg, err_msg);
+ }
+ }
+
return ira->codegen->invalid_inst_gen;
}
- cache_buf(cache_hash, &cimport_scope->buf);
-
- // Set this because we're not adding any files before checking for a hit.
- cache_hash->force_check_manifest = true;
-
- Buf tmp_c_file_digest = BUF_INIT;
- buf_resize(&tmp_c_file_digest, 0);
- if ((err = cache_hit(cache_hash, &tmp_c_file_digest))) {
- if (err != ErrorInvalidFormat) {
- ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to check cache: %s", err_str(err)));
- return ira->codegen->invalid_inst_gen;
- }
- }
- ira->codegen->caches_to_release.append(cache_hash);
-
- Buf *out_zig_dir = buf_alloc();
- Buf *out_zig_path = buf_alloc();
- if (buf_len(&tmp_c_file_digest) == 0 || cache_hash->files.length == 0) {
- // Cache Miss
- Buf *tmp_c_file_dir = buf_sprintf("%s" OS_SEP "o" OS_SEP "%s",
- buf_ptr(ira->codegen->cache_dir), buf_ptr(&cache_hash->b64_digest));
- Buf *resolve_paths[] = {
- tmp_c_file_dir,
- buf_create_from_str("cimport.h"),
- };
- Buf tmp_c_file_path = os_path_resolve(resolve_paths, 2);
-
- if ((err = os_make_path(tmp_c_file_dir))) {
- ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to make dir: %s", err_str(err)));
- return ira->codegen->invalid_inst_gen;
- }
-
- if ((err = os_write_file(&tmp_c_file_path, &cimport_scope->buf))) {
- ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to write .h file: %s", err_str(err)));
- return ira->codegen->invalid_inst_gen;
- }
- if (ira->codegen->verbose_cimport) {
- fprintf(stderr, "@cImport source: %s\n", buf_ptr(&tmp_c_file_path));
- }
-
- Buf *tmp_dep_file = buf_sprintf("%s.d", buf_ptr(&tmp_c_file_path));
-
- ZigList clang_argv = {0};
-
- add_cc_args(ira->codegen, clang_argv, buf_ptr(tmp_dep_file), true, FileExtC);
-
- clang_argv.append(buf_ptr(&tmp_c_file_path));
-
- if (ira->codegen->verbose_cc) {
- fprintf(stderr, "clang");
- for (size_t i = 0; i < clang_argv.length; i += 1) {
- fprintf(stderr, " %s", clang_argv.at(i));
- }
- fprintf(stderr, "\n");
- }
-
- clang_argv.append(nullptr); // to make the [start...end] argument work
-
- Stage2ErrorMsg *errors_ptr;
- size_t errors_len;
- Stage2Ast *ast;
-
- const char *resources_path = buf_ptr(ira->codegen->zig_c_headers_dir);
-
- if ((err = stage2_translate_c(&ast, &errors_ptr, &errors_len,
- &clang_argv.at(0), &clang_argv.last(), resources_path)))
- {
- if (err != ErrorCCompileErrors) {
- ir_add_error_node(ira, node, buf_sprintf("C import failed: %s", err_str(err)));
- return ira->codegen->invalid_inst_gen;
- }
-
- ErrorMsg *parent_err_msg = ir_add_error_node(ira, node, buf_sprintf("C import failed"));
- if (ira->codegen->libc_link_lib == nullptr) {
- add_error_note(ira->codegen, parent_err_msg, node,
- buf_sprintf("libc headers not available; compilation does not link against libc"));
- }
- for (size_t i = 0; i < errors_len; i += 1) {
- Stage2ErrorMsg *clang_err = &errors_ptr[i];
- // Clang can emit "too many errors, stopping now", in which case `source` and `filename_ptr` are null
- if (clang_err->source && clang_err->filename_ptr) {
- ErrorMsg *err_msg = err_msg_create_with_offset(
- clang_err->filename_ptr ?
- buf_create_from_mem(clang_err->filename_ptr, clang_err->filename_len) : buf_alloc(),
- clang_err->line, clang_err->column, clang_err->offset, clang_err->source,
- buf_create_from_mem(clang_err->msg_ptr, clang_err->msg_len));
- err_msg_add_note(parent_err_msg, err_msg);
- }
- }
-
- return ira->codegen->invalid_inst_gen;
- }
- if (ira->codegen->verbose_cimport) {
- fprintf(stderr, "@cImport .d file: %s\n", buf_ptr(tmp_dep_file));
- }
-
- if ((err = cache_add_dep_file(cache_hash, tmp_dep_file, false))) {
- ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to parse .d file: %s", err_str(err)));
- return ira->codegen->invalid_inst_gen;
- }
- if ((err = cache_final(cache_hash, &tmp_c_file_digest))) {
- ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to finalize cache: %s", err_str(err)));
- return ira->codegen->invalid_inst_gen;
- }
-
- ir_cimport_cache_paths(ira->codegen->cache_dir, &tmp_c_file_digest, out_zig_dir, out_zig_path);
- if ((err = os_make_path(out_zig_dir))) {
- ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to make output dir: %s", err_str(err)));
- return ira->codegen->invalid_inst_gen;
- }
- FILE *out_file = fopen(buf_ptr(out_zig_path), "wb");
- if (out_file == nullptr) {
- ir_add_error_node(ira, node,
- buf_sprintf("C import failed: unable to open output file: %s", strerror(errno)));
- return ira->codegen->invalid_inst_gen;
- }
- stage2_render_ast(ast, out_file);
- if (fclose(out_file) != 0) {
- ir_add_error_node(ira, node,
- buf_sprintf("C import failed: unable to write to output file: %s", strerror(errno)));
- return ira->codegen->invalid_inst_gen;
- }
-
- if (ira->codegen->verbose_cimport) {
- fprintf(stderr, "@cImport output: %s\n", buf_ptr(out_zig_path));
- }
-
- } else {
- // Cache Hit
- ir_cimport_cache_paths(ira->codegen->cache_dir, &tmp_c_file_digest, out_zig_dir, out_zig_path);
- if (ira->codegen->verbose_cimport) {
- fprintf(stderr, "@cImport cache hit: %s\n", buf_ptr(out_zig_path));
- }
- }
+ Buf *out_zig_path = buf_create_from_mem(out_zig_path_ptr, out_zig_path_len);
Buf *import_code = buf_alloc();
if ((err = file_fetch(ira->codegen, out_zig_path, import_code))) {
diff --git a/src/ir.hpp b/src/stage1/ir.hpp
similarity index 100%
rename from src/ir.hpp
rename to src/stage1/ir.hpp
diff --git a/src/ir_print.cpp b/src/stage1/ir_print.cpp
similarity index 100%
rename from src/ir_print.cpp
rename to src/stage1/ir_print.cpp
diff --git a/src/ir_print.hpp b/src/stage1/ir_print.hpp
similarity index 100%
rename from src/ir_print.hpp
rename to src/stage1/ir_print.hpp
diff --git a/src/list.hpp b/src/stage1/list.hpp
similarity index 100%
rename from src/list.hpp
rename to src/stage1/list.hpp
diff --git a/src/mem.cpp b/src/stage1/mem.cpp
similarity index 63%
rename from src/mem.cpp
rename to src/stage1/mem.cpp
index 51ee9a27ee..48dbd791de 100644
--- a/src/mem.cpp
+++ b/src/stage1/mem.cpp
@@ -7,7 +7,6 @@
#include "config.h"
#include "mem.hpp"
-#include "mem_profile.hpp"
#include "heap.hpp"
namespace mem {
@@ -22,16 +21,4 @@ void deinit() {
heap::bootstrap_allocator_state.deinit();
}
-#ifdef ZIG_ENABLE_MEM_PROFILE
-void print_report(FILE *file) {
- heap::c_allocator_state.print_report(file);
- intern_counters.print_report(file);
-}
-#endif
-
-#ifdef ZIG_ENABLE_MEM_PROFILE
-bool report_print = false;
-FILE *report_file{nullptr};
-#endif
-
} // namespace mem
diff --git a/src/mem.hpp b/src/stage1/mem.hpp
similarity index 95%
rename from src/mem.hpp
rename to src/stage1/mem.hpp
index 9e262b7d53..3008febbde 100644
--- a/src/mem.hpp
+++ b/src/stage1/mem.hpp
@@ -135,15 +135,6 @@ protected:
virtual void internal_deallocate(const TypeInfo &info, void *ptr, size_t count) = 0;
};
-#ifdef ZIG_ENABLE_MEM_PROFILE
-void print_report(FILE *file = nullptr);
-
-// global memory report flag
-extern bool report_print;
-// global memory report default destination
-extern FILE *report_file;
-#endif
-
} // namespace mem
#endif
diff --git a/src/mem_hash_map.hpp b/src/stage1/mem_hash_map.hpp
similarity index 100%
rename from src/mem_hash_map.hpp
rename to src/stage1/mem_hash_map.hpp
diff --git a/src/mem_list.hpp b/src/stage1/mem_list.hpp
similarity index 100%
rename from src/mem_list.hpp
rename to src/stage1/mem_list.hpp
diff --git a/src/stage1/mem_type_info.hpp b/src/stage1/mem_type_info.hpp
new file mode 100644
index 0000000000..d8a7933268
--- /dev/null
+++ b/src/stage1/mem_type_info.hpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2020 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#ifndef ZIG_MEM_TYPE_INFO_HPP
+#define ZIG_MEM_TYPE_INFO_HPP
+
+#include "config.h"
+
+namespace mem {
+
+struct TypeInfo {
+ size_t size;
+ size_t alignment;
+
+ template
+ static constexpr TypeInfo make() {
+ return {sizeof(T), alignof(T)};
+ }
+};
+
+} // namespace mem
+
+#endif
diff --git a/src/os.cpp b/src/stage1/os.cpp
similarity index 100%
rename from src/os.cpp
rename to src/stage1/os.cpp
diff --git a/src/os.hpp b/src/stage1/os.hpp
similarity index 100%
rename from src/os.hpp
rename to src/stage1/os.hpp
diff --git a/src/parse_f128.c b/src/stage1/parse_f128.c
similarity index 100%
rename from src/parse_f128.c
rename to src/stage1/parse_f128.c
diff --git a/src/parse_f128.h b/src/stage1/parse_f128.h
similarity index 100%
rename from src/parse_f128.h
rename to src/stage1/parse_f128.h
diff --git a/src/parser.cpp b/src/stage1/parser.cpp
similarity index 100%
rename from src/parser.cpp
rename to src/stage1/parser.cpp
diff --git a/src/parser.hpp b/src/stage1/parser.hpp
similarity index 100%
rename from src/parser.hpp
rename to src/stage1/parser.hpp
diff --git a/src/range_set.cpp b/src/stage1/range_set.cpp
similarity index 100%
rename from src/range_set.cpp
rename to src/stage1/range_set.cpp
diff --git a/src/range_set.hpp b/src/stage1/range_set.hpp
similarity index 100%
rename from src/range_set.hpp
rename to src/stage1/range_set.hpp
diff --git a/src/softfloat.hpp b/src/stage1/softfloat.hpp
similarity index 100%
rename from src/softfloat.hpp
rename to src/stage1/softfloat.hpp
diff --git a/src/softfloat_ext.cpp b/src/stage1/softfloat_ext.cpp
similarity index 100%
rename from src/softfloat_ext.cpp
rename to src/stage1/softfloat_ext.cpp
diff --git a/src/softfloat_ext.hpp b/src/stage1/softfloat_ext.hpp
similarity index 100%
rename from src/softfloat_ext.hpp
rename to src/stage1/softfloat_ext.hpp
diff --git a/src/stage1/stage1.cpp b/src/stage1/stage1.cpp
new file mode 100644
index 0000000000..1034f9ff88
--- /dev/null
+++ b/src/stage1/stage1.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2020 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#include "stage1.h"
+#include "os.hpp"
+#include "all_types.hpp"
+#include "codegen.hpp"
+
+void zig_stage1_os_init(void) {
+ os_init();
+ mem::init();
+ init_all_targets();
+}
+
+struct ZigStage1 *zig_stage1_create(BuildMode optimize_mode,
+ const char *main_pkg_path_ptr, size_t main_pkg_path_len,
+ const char *root_src_path_ptr, size_t root_src_path_len,
+ const char *zig_lib_dir_ptr, size_t zig_lib_dir_len,
+ const ZigTarget *target, bool is_test_build)
+{
+ Buf *main_pkg_path = (main_pkg_path_len == 0) ?
+ nullptr : buf_create_from_mem(main_pkg_path_ptr, main_pkg_path_len);
+ Buf *root_src_path = buf_create_from_mem(root_src_path_ptr, root_src_path_len);
+ Buf *zig_lib_dir = buf_create_from_mem(zig_lib_dir_ptr, zig_lib_dir_len);
+ CodeGen *g = codegen_create(main_pkg_path, root_src_path, target, optimize_mode,
+ zig_lib_dir, is_test_build);
+ return &g->stage1;
+}
+
+void zig_stage1_destroy(struct ZigStage1 *stage1) {
+ CodeGen *codegen = reinterpret_cast(stage1);
+ codegen_destroy(codegen);
+}
+
+static void add_package(CodeGen *g, ZigStage1Pkg *stage1_pkg, ZigPackage *pkg) {
+ for (size_t i = 0; i < stage1_pkg->children_len; i += 1) {
+ ZigStage1Pkg *child_cli_pkg = stage1_pkg->children_ptr[i];
+
+ Buf *dirname = buf_alloc();
+ Buf *basename = buf_alloc();
+ os_path_split(buf_create_from_mem(child_cli_pkg->path_ptr, child_cli_pkg->path_len), dirname, basename);
+
+ ZigPackage *child_pkg = codegen_create_package(g, buf_ptr(dirname), buf_ptr(basename),
+ buf_ptr(buf_sprintf("%s.%.*s", buf_ptr(&pkg->pkg_path),
+ (int)child_cli_pkg->name_len, child_cli_pkg->name_ptr)));
+ auto entry = pkg->package_table.put_unique(
+ buf_create_from_mem(child_cli_pkg->name_ptr, child_cli_pkg->name_len),
+ child_pkg);
+ if (entry) {
+ ZigPackage *existing_pkg = entry->value;
+ Buf *full_path = buf_alloc();
+ os_path_join(&existing_pkg->root_src_dir, &existing_pkg->root_src_path, full_path);
+ fprintf(stderr, "Unable to add package '%.*s'->'%.*s': already exists as '%s'\n",
+ (int)child_cli_pkg->name_len, child_cli_pkg->name_ptr,
+ (int)child_cli_pkg->path_len, child_cli_pkg->path_ptr,
+ buf_ptr(full_path));
+ exit(EXIT_FAILURE);
+ }
+
+ add_package(g, child_cli_pkg, child_pkg);
+ }
+}
+
+void zig_stage1_build_object(struct ZigStage1 *stage1) {
+ CodeGen *g = reinterpret_cast(stage1);
+
+ g->root_out_name = buf_create_from_mem(stage1->root_name_ptr, stage1->root_name_len);
+ buf_init_from_mem(&g->o_file_output_path, stage1->emit_o_ptr, stage1->emit_o_len);
+ buf_init_from_mem(&g->h_file_output_path, stage1->emit_h_ptr, stage1->emit_h_len);
+ buf_init_from_mem(&g->asm_file_output_path, stage1->emit_asm_ptr, stage1->emit_asm_len);
+ buf_init_from_mem(&g->llvm_ir_file_output_path, stage1->emit_llvm_ir_ptr, stage1->emit_llvm_ir_len);
+ buf_init_from_mem(&g->analysis_json_output_path, stage1->emit_analysis_json_ptr, stage1->emit_analysis_json_len);
+ buf_init_from_mem(&g->docs_output_path, stage1->emit_docs_ptr, stage1->emit_docs_len);
+
+ if (stage1->builtin_zig_path_len != 0) {
+ g->builtin_zig_path = buf_create_from_mem(stage1->builtin_zig_path_ptr, stage1->builtin_zig_path_len);
+ }
+ if (stage1->test_filter_len != 0) {
+ g->test_filter = buf_create_from_mem(stage1->test_filter_ptr, stage1->test_filter_len);
+ }
+ if (stage1->test_name_prefix_len != 0) {
+ g->test_name_prefix = buf_create_from_mem(stage1->test_name_prefix_ptr, stage1->test_name_prefix_len);
+ }
+
+ g->link_mode_dynamic = stage1->link_mode_dynamic;
+ g->dll_export_fns = stage1->dll_export_fns;
+ g->have_pic = stage1->pic;
+ g->have_stack_probing = stage1->enable_stack_probing;
+ g->is_single_threaded = stage1->is_single_threaded;
+ g->valgrind_enabled = stage1->valgrind_enabled;
+ g->link_libc = stage1->link_libc;
+ g->link_libcpp = stage1->link_libcpp;
+ g->function_sections = stage1->function_sections;
+
+ g->subsystem = stage1->subsystem;
+
+ g->enable_time_report = stage1->enable_time_report;
+ g->enable_stack_report = stage1->enable_stack_report;
+ g->test_is_evented = stage1->test_is_evented;
+
+ g->verbose_tokenize = stage1->verbose_tokenize;
+ g->verbose_ast = stage1->verbose_ast;
+ g->verbose_ir = stage1->verbose_ir;
+ g->verbose_llvm_ir = stage1->verbose_llvm_ir;
+ g->verbose_cimport = stage1->verbose_cimport;
+ g->verbose_llvm_cpu_features = stage1->verbose_llvm_cpu_features;
+
+ g->err_color = stage1->err_color;
+ g->code_model = stage1->code_model;
+
+ {
+ g->strip_debug_symbols = stage1->strip;
+ if (!target_has_debug_info(g->zig_target)) {
+ g->strip_debug_symbols = true;
+ }
+ }
+
+ g->main_progress_node = stage1->main_progress_node;
+
+ add_package(g, stage1->root_pkg, g->main_pkg);
+
+ codegen_build_object(g);
+}
diff --git a/src/stage1/stage1.h b/src/stage1/stage1.h
new file mode 100644
index 0000000000..412118a6fa
--- /dev/null
+++ b/src/stage1/stage1.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2020 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+// This file deals with exposing stage1 C++ code to stage2 Zig code.
+
+#ifndef ZIG_STAGE1_H
+#define ZIG_STAGE1_H
+
+#include "zig_llvm.h"
+
+#include
+
+#ifdef __cplusplus
+#define ZIG_EXTERN_C extern "C"
+#else
+#define ZIG_EXTERN_C
+#endif
+
+// ABI warning
+enum ErrColor {
+ ErrColorAuto,
+ ErrColorOff,
+ ErrColorOn,
+};
+
+// ABI warning
+enum CodeModel {
+ CodeModelDefault,
+ CodeModelTiny,
+ CodeModelSmall,
+ CodeModelKernel,
+ CodeModelMedium,
+ CodeModelLarge,
+};
+
+// ABI warning
+enum TargetSubsystem {
+ TargetSubsystemConsole,
+ TargetSubsystemWindows,
+ TargetSubsystemPosix,
+ TargetSubsystemNative,
+ TargetSubsystemEfiApplication,
+ TargetSubsystemEfiBootServiceDriver,
+ TargetSubsystemEfiRom,
+ TargetSubsystemEfiRuntimeDriver,
+
+ // This means Zig should infer the subsystem.
+ // It's last so that the indexes of other items can line up
+ // with the enum in builtin.zig.
+ TargetSubsystemAuto
+};
+
+
+// ABI warning
+// Synchronize with target.cpp::os_list
+enum Os {
+ OsFreestanding,
+ OsAnanas,
+ OsCloudABI,
+ OsDragonFly,
+ OsFreeBSD,
+ OsFuchsia,
+ OsIOS,
+ OsKFreeBSD,
+ OsLinux,
+ OsLv2, // PS3
+ OsMacOSX,
+ OsNetBSD,
+ OsOpenBSD,
+ OsSolaris,
+ OsWindows,
+ OsHaiku,
+ OsMinix,
+ OsRTEMS,
+ OsNaCl, // Native Client
+ OsCNK, // BG/P Compute-Node Kernel
+ OsAIX,
+ OsCUDA, // NVIDIA CUDA
+ OsNVCL, // NVIDIA OpenCL
+ OsAMDHSA, // AMD HSA Runtime
+ OsPS4,
+ OsELFIAMCU,
+ OsTvOS, // Apple tvOS
+ OsWatchOS, // Apple watchOS
+ OsMesa3D,
+ OsContiki,
+ OsAMDPAL,
+ OsHermitCore,
+ OsHurd,
+ OsWASI,
+ OsEmscripten,
+ OsUefi,
+ OsOther,
+};
+
+// ABI warning
+struct ZigTarget {
+ enum ZigLLVM_ArchType arch;
+ enum Os os;
+ enum ZigLLVM_EnvironmentType abi;
+
+ bool is_native_os;
+ bool is_native_cpu;
+
+ const char *llvm_cpu_name;
+ const char *llvm_cpu_features;
+};
+
+// ABI warning
+struct Stage2Progress;
+// ABI warning
+struct Stage2ProgressNode;
+
+enum BuildMode {
+ BuildModeDebug,
+ BuildModeSafeRelease,
+ BuildModeFastRelease,
+ BuildModeSmallRelease,
+};
+
+
+struct ZigStage1Pkg {
+ const char *name_ptr;
+ size_t name_len;
+
+ const char *path_ptr;
+ size_t path_len;
+
+ struct ZigStage1Pkg **children_ptr;
+ size_t children_len;
+
+ struct ZigStage1Pkg *parent;
+};
+
+// This struct is used by both main.cpp and stage1.zig.
+struct ZigStage1 {
+ const char *root_name_ptr;
+ size_t root_name_len;
+
+ const char *emit_o_ptr;
+ size_t emit_o_len;
+
+ const char *emit_h_ptr;
+ size_t emit_h_len;
+
+ const char *emit_asm_ptr;
+ size_t emit_asm_len;
+
+ const char *emit_llvm_ir_ptr;
+ size_t emit_llvm_ir_len;
+
+ const char *emit_analysis_json_ptr;
+ size_t emit_analysis_json_len;
+
+ const char *emit_docs_ptr;
+ size_t emit_docs_len;
+
+ const char *builtin_zig_path_ptr;
+ size_t builtin_zig_path_len;
+
+ const char *test_filter_ptr;
+ size_t test_filter_len;
+
+ const char *test_name_prefix_ptr;
+ size_t test_name_prefix_len;
+
+ void *userdata;
+ struct ZigStage1Pkg *root_pkg;
+ struct Stage2ProgressNode *main_progress_node;
+
+ enum CodeModel code_model;
+ enum TargetSubsystem subsystem;
+ enum ErrColor err_color;
+
+ bool pic;
+ bool link_libc;
+ bool link_libcpp;
+ bool strip;
+ bool is_single_threaded;
+ bool dll_export_fns;
+ bool link_mode_dynamic;
+ bool valgrind_enabled;
+ bool function_sections;
+ bool enable_stack_probing;
+ bool enable_time_report;
+ bool enable_stack_report;
+ bool test_is_evented;
+ bool verbose_tokenize;
+ bool verbose_ast;
+ bool verbose_ir;
+ bool verbose_llvm_ir;
+ bool verbose_cimport;
+ bool verbose_llvm_cpu_features;
+
+ // Set by stage1
+ bool have_c_main;
+ bool have_winmain;
+ bool have_wwinmain;
+ bool have_winmain_crt_startup;
+ bool have_wwinmain_crt_startup;
+ bool have_dllmain_crt_startup;
+};
+
+ZIG_EXTERN_C void zig_stage1_os_init(void);
+
+ZIG_EXTERN_C struct ZigStage1 *zig_stage1_create(enum BuildMode optimize_mode,
+ const char *main_pkg_path_ptr, size_t main_pkg_path_len,
+ const char *root_src_path_ptr, size_t root_src_path_len,
+ const char *zig_lib_dir_ptr, size_t zig_lib_dir_len,
+ const struct ZigTarget *target, bool is_test_build);
+
+ZIG_EXTERN_C void zig_stage1_build_object(struct ZigStage1 *);
+
+ZIG_EXTERN_C void zig_stage1_destroy(struct ZigStage1 *);
+
+#endif
diff --git a/src/stage1/stage2.h b/src/stage1/stage2.h
new file mode 100644
index 0000000000..886a4c2660
--- /dev/null
+++ b/src/stage1/stage2.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2019 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+// This file deals with exposing stage2 Zig code to stage1 C++ code.
+
+#ifndef ZIG_STAGE2_H
+#define ZIG_STAGE2_H
+
+#include
+#include
+#include
+
+#include "stage1.h"
+
+#ifdef __cplusplus
+#define ZIG_EXTERN_C extern "C"
+#else
+#define ZIG_EXTERN_C
+#endif
+
+#if defined(_MSC_VER)
+#define ZIG_ATTRIBUTE_NORETURN __declspec(noreturn)
+#else
+#define ZIG_ATTRIBUTE_NORETURN __attribute__((noreturn))
+#endif
+
+// ABI warning: the types and declarations in this file must match both those in
+// stage2.cpp and src/stage1.zig.
+
+// ABI warning
+enum Error {
+ ErrorNone,
+ ErrorNoMem,
+ ErrorInvalidFormat,
+ ErrorSemanticAnalyzeFail,
+ ErrorAccess,
+ ErrorInterrupted,
+ ErrorSystemResources,
+ ErrorFileNotFound,
+ ErrorFileSystem,
+ ErrorFileTooBig,
+ ErrorDivByZero,
+ ErrorOverflow,
+ ErrorPathAlreadyExists,
+ ErrorUnexpected,
+ ErrorExactDivRemainder,
+ ErrorNegativeDenominator,
+ ErrorShiftedOutOneBits,
+ ErrorCCompileErrors,
+ ErrorEndOfFile,
+ ErrorIsDir,
+ ErrorNotDir,
+ ErrorUnsupportedOperatingSystem,
+ ErrorSharingViolation,
+ ErrorPipeBusy,
+ ErrorPrimitiveTypeNotFound,
+ ErrorCacheUnavailable,
+ ErrorPathTooLong,
+ ErrorCCompilerCannotFindFile,
+ ErrorNoCCompilerInstalled,
+ ErrorReadingDepFile,
+ ErrorInvalidDepFile,
+ ErrorMissingArchitecture,
+ ErrorMissingOperatingSystem,
+ ErrorUnknownArchitecture,
+ ErrorUnknownOperatingSystem,
+ ErrorUnknownABI,
+ ErrorInvalidFilename,
+ ErrorDiskQuota,
+ ErrorDiskSpace,
+ ErrorUnexpectedWriteFailure,
+ ErrorUnexpectedSeekFailure,
+ ErrorUnexpectedFileTruncationFailure,
+ ErrorUnimplemented,
+ ErrorOperationAborted,
+ ErrorBrokenPipe,
+ ErrorNoSpaceLeft,
+ ErrorNotLazy,
+ ErrorIsAsync,
+ ErrorImportOutsidePkgPath,
+ ErrorUnknownCpu,
+ ErrorUnknownCpuFeature,
+ ErrorInvalidCpuFeatures,
+ ErrorInvalidLlvmCpuFeaturesFormat,
+ ErrorUnknownApplicationBinaryInterface,
+ ErrorASTUnitFailure,
+ ErrorBadPathName,
+ ErrorSymLinkLoop,
+ ErrorProcessFdQuotaExceeded,
+ ErrorSystemFdQuotaExceeded,
+ ErrorNoDevice,
+ ErrorDeviceBusy,
+ ErrorUnableToSpawnCCompiler,
+ ErrorCCompilerExitCode,
+ ErrorCCompilerCrashed,
+ ErrorCCompilerCannotFindHeaders,
+ ErrorLibCRuntimeNotFound,
+ ErrorLibCStdLibHeaderNotFound,
+ ErrorLibCKernel32LibNotFound,
+ ErrorUnsupportedArchitecture,
+ ErrorWindowsSdkNotFound,
+ ErrorUnknownDynamicLinkerPath,
+ ErrorTargetHasNoDynamicLinker,
+ ErrorInvalidAbiVersion,
+ ErrorInvalidOperatingSystemVersion,
+ ErrorUnknownClangOption,
+ ErrorNestedResponseFile,
+ ErrorZigIsTheCCompiler,
+ ErrorFileBusy,
+ ErrorLocked,
+};
+
+// ABI warning
+struct Stage2ErrorMsg {
+ const char *filename_ptr; // can be null
+ size_t filename_len;
+ const char *msg_ptr;
+ size_t msg_len;
+ const char *source; // valid until the ASTUnit is freed. can be null
+ unsigned line; // 0 based
+ unsigned column; // 0 based
+ unsigned offset; // byte offset into source
+};
+
+// ABI warning
+ZIG_EXTERN_C ZIG_ATTRIBUTE_NORETURN void stage2_panic(const char *ptr, size_t len);
+
+// ABI warning
+ZIG_EXTERN_C struct Stage2Progress *stage2_progress_create(void);
+// ABI warning
+ZIG_EXTERN_C void stage2_progress_disable_tty(struct Stage2Progress *progress);
+// ABI warning
+ZIG_EXTERN_C void stage2_progress_destroy(struct Stage2Progress *progress);
+// ABI warning
+ZIG_EXTERN_C struct Stage2ProgressNode *stage2_progress_start_root(struct Stage2Progress *progress,
+ const char *name_ptr, size_t name_len, size_t estimated_total_items);
+// ABI warning
+ZIG_EXTERN_C struct Stage2ProgressNode *stage2_progress_start(struct Stage2ProgressNode *node,
+ const char *name_ptr, size_t name_len, size_t estimated_total_items);
+// ABI warning
+ZIG_EXTERN_C void stage2_progress_end(struct Stage2ProgressNode *node);
+// ABI warning
+ZIG_EXTERN_C void stage2_progress_complete_one(struct Stage2ProgressNode *node);
+// ABI warning
+ZIG_EXTERN_C void stage2_progress_update_node(struct Stage2ProgressNode *node,
+ size_t completed_count, size_t estimated_total_items);
+
+// ABI warning
+struct Stage2SemVer {
+ uint32_t major;
+ uint32_t minor;
+ uint32_t patch;
+};
+
+// ABI warning
+ZIG_EXTERN_C enum Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu,
+ const char *dynamic_linker);
+
+// ABI warning
+ZIG_EXTERN_C const char *stage2_fetch_file(struct ZigStage1 *stage1, const char *path_ptr, size_t path_len,
+ size_t *result_len);
+
+// ABI warning
+ZIG_EXTERN_C Error stage2_cimport(struct ZigStage1 *stage1, const char *c_src_ptr, size_t c_src_len,
+ const char **out_zig_path_ptr, size_t *out_zig_path_len,
+ struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len);
+
+// ABI warning
+ZIG_EXTERN_C const char *stage2_add_link_lib(struct ZigStage1 *stage1,
+ const char *lib_name_ptr, size_t lib_name_len,
+ const char *symbol_name_ptr, size_t symbol_name_len);
+
+#endif
diff --git a/src/target.cpp b/src/stage1/target.cpp
similarity index 88%
rename from src/target.cpp
rename to src/stage1/target.cpp
index dff134a01d..433c988a01 100644
--- a/src/target.cpp
+++ b/src/stage1/target.cpp
@@ -10,8 +10,6 @@
#include "target.hpp"
#include "util.hpp"
#include "os.hpp"
-#include "compiler.hpp"
-#include "glibc.hpp"
#include
@@ -346,33 +344,6 @@ const char *target_abi_name(ZigLLVM_EnvironmentType abi) {
return ZigLLVMGetEnvironmentTypeName(abi);
}
-Error target_parse_glibc_version(Stage2SemVer *glibc_ver, const char *text) {
- glibc_ver->major = 2;
- glibc_ver->minor = 0;
- glibc_ver->patch = 0;
- SplitIterator it = memSplit(str(text), str("GLIBC_."));
- {
- Optional> opt_component = SplitIterator_next(&it);
- if (!opt_component.is_some) return ErrorUnknownABI;
- glibc_ver->major = strtoul(buf_ptr(buf_create_from_slice(opt_component.value)), nullptr, 10);
- }
- {
- Optional> opt_component = SplitIterator_next(&it);
- if (!opt_component.is_some) return ErrorNone;
- glibc_ver->minor = strtoul(buf_ptr(buf_create_from_slice(opt_component.value)), nullptr, 10);
- }
- {
- Optional> opt_component = SplitIterator_next(&it);
- if (!opt_component.is_some) return ErrorNone;
- glibc_ver->patch = strtoul(buf_ptr(buf_create_from_slice(opt_component.value)), nullptr, 10);
- }
- return ErrorNone;
-}
-
-void target_init_default_glibc_version(ZigTarget *target) {
- *target->glibc_or_darwin_version = {2, 17, 0};
-}
-
Error target_parse_arch(ZigLLVM_ArchType *out_arch, const char *arch_ptr, size_t arch_len) {
*out_arch = ZigLLVM_UnknownArch;
for (size_t arch_i = 0; arch_i < array_length(arch_list); arch_i += 1) {
@@ -409,10 +380,6 @@ Error target_parse_abi(ZigLLVM_EnvironmentType *out_abi, const char *abi_ptr, si
return ErrorUnknownABI;
}
-Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu, const char *dynamic_linker) {
- return stage2_target_parse(target, triple, mcpu, dynamic_linker);
-}
-
const char *target_arch_name(ZigLLVM_ArchType arch) {
return ZigLLVMGetArchTypeName(arch);
}
@@ -437,7 +404,7 @@ void target_triple_llvm(Buf *triple, const ZigTarget *target) {
buf_resize(triple, 0);
buf_appendf(triple, "%s-%s-%s-%s",
ZigLLVMGetArchTypeName(target->arch),
- ZigLLVMGetVendorTypeName(target->vendor),
+ ZigLLVMGetVendorTypeName(ZigLLVM_UnknownVendor),
ZigLLVMGetOSTypeName(get_llvm_os_type(target->os)),
ZigLLVMGetEnvironmentTypeName(target->abi));
}
@@ -756,66 +723,6 @@ const char *target_llvm_ir_file_ext(const ZigTarget *target) {
return ".ll";
}
-const char *target_exe_file_ext(const ZigTarget *target) {
- if (target->os == OsWindows) {
- return ".exe";
- } else if (target->os == OsUefi) {
- return ".efi";
- } else if (target_is_wasm(target)) {
- return ".wasm";
- } else {
- return "";
- }
-}
-
-const char *target_lib_file_prefix(const ZigTarget *target) {
- if ((target->os == OsWindows && !target_abi_is_gnu(target->abi)) ||
- target->os == OsUefi ||
- target_is_wasm(target))
- {
- return "";
- } else {
- return "lib";
- }
-}
-
-const char *target_lib_file_ext(const ZigTarget *target, bool is_static, bool is_versioned,
- size_t version_major, size_t version_minor, size_t version_patch)
-{
- if (target_is_wasm(target)) {
- return ".wasm";
- }
- if (target->os == OsWindows || target->os == OsUefi) {
- if (is_static) {
- if (target->os == OsWindows && target_abi_is_gnu(target->abi)) {
- return ".a";
- } else {
- return ".lib";
- }
- } else {
- return ".dll";
- }
- } else {
- if (is_static) {
- return ".a";
- } else if (target_os_is_darwin(target->os)) {
- if (is_versioned) {
- return buf_ptr(buf_sprintf(".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib",
- version_major, version_minor, version_patch));
- } else {
- return ".dylib";
- }
- } else {
- if (is_versioned) {
- return buf_ptr(buf_sprintf(".so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize,
- version_major, version_minor, version_patch));
- } else {
- return ".so";
- }
- }
- }
-}
-
bool target_is_android(const ZigTarget *target) {
return target->abi == ZigLLVM_Android;
}
@@ -992,40 +899,6 @@ bool target_os_requires_libc(Os os) {
return (target_os_is_darwin(os) || os == OsFreeBSD || os == OsNetBSD || os == OsDragonFly);
}
-bool target_supports_fpic(const ZigTarget *target) {
- // This is not whether the target supports Position Independent Code, but whether the -fPIC
- // C compiler argument is valid.
- return target->os != OsWindows;
-}
-
-bool target_supports_clang_march_native(const ZigTarget *target) {
- // Whether clang supports -march=native on this target.
- // Arguably it should always work, but in reality it gives:
- // error: the clang compiler does not support '-march=native'
- // If we move CPU detection logic into Zig itelf, we will not need this,
- // instead we will always pass target features and CPU configuration explicitly.
- return target->arch != ZigLLVM_aarch64 &&
- target->arch != ZigLLVM_aarch64_be;
-}
-
-bool target_supports_stack_probing(const ZigTarget *target) {
- return target->os != OsWindows && target->os != OsUefi && (target->arch == ZigLLVM_x86 || target->arch == ZigLLVM_x86_64);
-}
-
-bool target_supports_sanitize_c(const ZigTarget *target) {
- return true;
-}
-
-bool target_requires_pic(const ZigTarget *target, bool linking_libc) {
- // This function returns whether non-pic code is completely invalid on the given target.
- return target_is_android(target) || target->os == OsWindows || target->os == OsUefi || target_os_requires_libc(target->os) ||
- (linking_libc && target_is_glibc(target));
-}
-
-bool target_requires_pie(const ZigTarget *target) {
- return target_is_android(target);
-}
-
bool target_is_glibc(const ZigTarget *target) {
return target->os == OsLinux && target_abi_is_gnu(target->abi);
}
@@ -1038,10 +911,6 @@ bool target_is_wasm(const ZigTarget *target) {
return target->arch == ZigLLVM_wasm32 || target->arch == ZigLLVM_wasm64;
}
-bool target_is_single_threaded(const ZigTarget *target) {
- return target_is_wasm(target);
-}
-
ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) {
if (arch == ZigLLVM_wasm32 || arch == ZigLLVM_wasm64) {
return ZigLLVM_Musl;
@@ -1276,7 +1145,6 @@ void target_libc_enum(size_t index, ZigTarget *out_target) {
out_target->arch = libcs_available[index].arch;
out_target->os = libcs_available[index].os;
out_target->abi = libcs_available[index].abi;
- out_target->vendor = ZigLLVM_UnknownVendor;
out_target->is_native_os = false;
out_target->is_native_cpu = false;
}
diff --git a/src/target.hpp b/src/stage1/target.hpp
similarity index 71%
rename from src/target.hpp
rename to src/stage1/target.hpp
index 5e44301fff..d34c2aeae5 100644
--- a/src/target.hpp
+++ b/src/stage1/target.hpp
@@ -12,22 +12,6 @@
struct Buf;
-enum TargetSubsystem {
- TargetSubsystemConsole,
- TargetSubsystemWindows,
- TargetSubsystemPosix,
- TargetSubsystemNative,
- TargetSubsystemEfiApplication,
- TargetSubsystemEfiBootServiceDriver,
- TargetSubsystemEfiRom,
- TargetSubsystemEfiRuntimeDriver,
-
- // This means Zig should infer the subsystem.
- // It's last so that the indexes of other items can line up
- // with the enum in builtin.zig.
- TargetSubsystemAuto
-};
-
enum CIntType {
CIntTypeShort,
CIntTypeUShort,
@@ -41,14 +25,10 @@ enum CIntType {
CIntTypeCount,
};
-Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu, const char *dynamic_linker);
Error target_parse_arch(ZigLLVM_ArchType *arch, const char *arch_ptr, size_t arch_len);
Error target_parse_os(Os *os, const char *os_ptr, size_t os_len);
Error target_parse_abi(ZigLLVM_EnvironmentType *abi, const char *abi_ptr, size_t abi_len);
-Error target_parse_glibc_version(Stage2SemVer *out, const char *text);
-void target_init_default_glibc_version(ZigTarget *target);
-
size_t target_arch_count(void);
ZigLLVM_ArchType target_arch_enum(size_t index);
const char *target_arch_name(ZigLLVM_ArchType arch);
@@ -85,10 +65,6 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id);
const char *target_o_file_ext(const ZigTarget *target);
const char *target_asm_file_ext(const ZigTarget *target);
const char *target_llvm_ir_file_ext(const ZigTarget *target);
-const char *target_exe_file_ext(const ZigTarget *target);
-const char *target_lib_file_prefix(const ZigTarget *target);
-const char *target_lib_file_ext(const ZigTarget *target, bool is_static, bool is_versioned,
- size_t version_major, size_t version_minor, size_t version_patch);
bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target);
ZigLLVM_OSType get_llvm_os_type(Os os_type);
@@ -104,10 +80,6 @@ bool target_can_build_libc(const ZigTarget *target);
const char *target_libc_generic_name(const ZigTarget *target);
bool target_is_libc_lib_name(const ZigTarget *target, const char *name);
bool target_is_libcpp_lib_name(const ZigTarget *target, const char *name);
-bool target_supports_fpic(const ZigTarget *target);
-bool target_supports_clang_march_native(const ZigTarget *target);
-bool target_requires_pic(const ZigTarget *target, bool linking_libc);
-bool target_requires_pie(const ZigTarget *target);
bool target_abi_is_gnu(ZigLLVM_EnvironmentType abi);
bool target_abi_is_musl(ZigLLVM_EnvironmentType abi);
bool target_is_glibc(const ZigTarget *target);
@@ -115,9 +87,6 @@ bool target_is_musl(const ZigTarget *target);
bool target_is_wasm(const ZigTarget *target);
bool target_is_riscv(const ZigTarget *target);
bool target_is_android(const ZigTarget *target);
-bool target_is_single_threaded(const ZigTarget *target);
-bool target_supports_stack_probing(const ZigTarget *target);
-bool target_supports_sanitize_c(const ZigTarget *target);
bool target_has_debug_info(const ZigTarget *target);
const char *target_arch_musl_name(ZigLLVM_ArchType arch);
diff --git a/src/tokenizer.cpp b/src/stage1/tokenizer.cpp
similarity index 99%
rename from src/tokenizer.cpp
rename to src/stage1/tokenizer.cpp
index 4415bdf431..fa14dd40fa 100644
--- a/src/tokenizer.cpp
+++ b/src/stage1/tokenizer.cpp
@@ -1225,9 +1225,6 @@ void tokenize(Buf *buf, Tokenization *out) {
invalid_char_error(&t, c);
break;
}
- if (t.radix != 16 && t.radix != 10) {
- invalid_char_error(&t, c);
- }
t.state = TokenizeStateNumberDot;
break;
}
@@ -1281,6 +1278,9 @@ void tokenize(Buf *buf, Tokenization *out) {
t.state = TokenizeStateStart;
continue;
}
+ if (t.radix != 16 && t.radix != 10) {
+ invalid_char_error(&t, c);
+ }
t.pos -= 1;
t.state = TokenizeStateFloatFractionNoUnderscore;
assert(t.cur_tok->id == TokenIdIntLiteral);
diff --git a/src/tokenizer.hpp b/src/stage1/tokenizer.hpp
similarity index 100%
rename from src/tokenizer.hpp
rename to src/stage1/tokenizer.hpp
diff --git a/src/util.cpp b/src/stage1/util.cpp
similarity index 100%
rename from src/util.cpp
rename to src/stage1/util.cpp
diff --git a/src/util.hpp b/src/stage1/util.hpp
similarity index 100%
rename from src/util.hpp
rename to src/stage1/util.hpp
diff --git a/src/util_base.hpp b/src/stage1/util_base.hpp
similarity index 100%
rename from src/util_base.hpp
rename to src/stage1/util_base.hpp
diff --git a/src/stage1/zig0.cpp b/src/stage1/zig0.cpp
new file mode 100644
index 0000000000..839ff0263f
--- /dev/null
+++ b/src/stage1/zig0.cpp
@@ -0,0 +1,529 @@
+/*
+ * Copyright (c) 2015 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+// This file is the entry point for zig0, which is *only* used to build
+// stage2, the self-hosted compiler, into an object file, which is then
+// linked by the same build system (cmake) that linked this binary.
+
+#include "stage1.h"
+#include "heap.hpp"
+#include "stage2.h"
+#include "target.hpp"
+#include "error.hpp"
+#include "util.hpp"
+#include "buffer.hpp"
+#include "os.hpp"
+
+#include
+#include
+
+static int print_error_usage(const char *arg0) {
+ fprintf(stderr, "See `%s --help` for detailed usage information\n", arg0);
+ return EXIT_FAILURE;
+}
+
+static int print_full_usage(const char *arg0, FILE *file, int return_code) {
+ fprintf(file,
+ "Usage: %s [options] builds an object file\n"
+ "\n"
+ "Options:\n"
+ " --color [auto|off|on] enable or disable colored error messages\n"
+ " --name [name] override output name\n"
+ " -femit-bin=[path] Output machine code\n"
+ " --pkg-begin [name] [path] make pkg available to import and push current pkg\n"
+ " --pkg-end pop current pkg\n"
+ " -ODebug build with optimizations on and safety off\n"
+ " -OReleaseFast build with optimizations on and safety off\n"
+ " -OReleaseSafe build with optimizations on and safety on\n"
+ " -OReleaseSmall build with size optimizations on and safety off\n"
+ " --single-threaded source may assume it is only used single-threaded\n"
+ " -dynamic create a shared library (.so; .dll; .dylib)\n"
+ " --strip exclude debug symbols\n"
+ " -target [name] -- see the targets command\n"
+ " -mcpu [cpu] specify target CPU and feature set\n"
+ " --verbose-tokenize enable compiler debug output for tokenization\n"
+ " --verbose-ast enable compiler debug output for AST parsing\n"
+ " --verbose-ir enable compiler debug output for Zig IR\n"
+ " --verbose-llvm-ir enable compiler debug output for LLVM IR\n"
+ " --verbose-cimport enable compiler debug output for C imports\n"
+ " --verbose-llvm-cpu-features enable compiler debug output for LLVM CPU features\n"
+ "\n"
+ , arg0);
+ return return_code;
+}
+
+static Os get_zig_os_type(ZigLLVM_OSType os_type) {
+ switch (os_type) {
+ case ZigLLVM_UnknownOS:
+ return OsFreestanding;
+ case ZigLLVM_Ananas:
+ return OsAnanas;
+ case ZigLLVM_CloudABI:
+ return OsCloudABI;
+ case ZigLLVM_DragonFly:
+ return OsDragonFly;
+ case ZigLLVM_FreeBSD:
+ return OsFreeBSD;
+ case ZigLLVM_Fuchsia:
+ return OsFuchsia;
+ case ZigLLVM_IOS:
+ return OsIOS;
+ case ZigLLVM_KFreeBSD:
+ return OsKFreeBSD;
+ case ZigLLVM_Linux:
+ return OsLinux;
+ case ZigLLVM_Lv2:
+ return OsLv2;
+ case ZigLLVM_Darwin:
+ case ZigLLVM_MacOSX:
+ return OsMacOSX;
+ case ZigLLVM_NetBSD:
+ return OsNetBSD;
+ case ZigLLVM_OpenBSD:
+ return OsOpenBSD;
+ case ZigLLVM_Solaris:
+ return OsSolaris;
+ case ZigLLVM_Win32:
+ return OsWindows;
+ case ZigLLVM_Haiku:
+ return OsHaiku;
+ case ZigLLVM_Minix:
+ return OsMinix;
+ case ZigLLVM_RTEMS:
+ return OsRTEMS;
+ case ZigLLVM_NaCl:
+ return OsNaCl;
+ case ZigLLVM_CNK:
+ return OsCNK;
+ case ZigLLVM_AIX:
+ return OsAIX;
+ case ZigLLVM_CUDA:
+ return OsCUDA;
+ case ZigLLVM_NVCL:
+ return OsNVCL;
+ case ZigLLVM_AMDHSA:
+ return OsAMDHSA;
+ case ZigLLVM_PS4:
+ return OsPS4;
+ case ZigLLVM_ELFIAMCU:
+ return OsELFIAMCU;
+ case ZigLLVM_TvOS:
+ return OsTvOS;
+ case ZigLLVM_WatchOS:
+ return OsWatchOS;
+ case ZigLLVM_Mesa3D:
+ return OsMesa3D;
+ case ZigLLVM_Contiki:
+ return OsContiki;
+ case ZigLLVM_AMDPAL:
+ return OsAMDPAL;
+ case ZigLLVM_HermitCore:
+ return OsHermitCore;
+ case ZigLLVM_Hurd:
+ return OsHurd;
+ case ZigLLVM_WASI:
+ return OsWASI;
+ case ZigLLVM_Emscripten:
+ return OsEmscripten;
+ }
+ zig_unreachable();
+}
+
+static void get_native_target(ZigTarget *target) {
+ // first zero initialize
+ *target = {};
+
+ ZigLLVM_OSType os_type;
+ ZigLLVM_ObjectFormatType oformat; // ignored; based on arch/os
+ ZigLLVM_VendorType trash;
+ ZigLLVMGetNativeTarget(
+ &target->arch,
+ &trash,
+ &os_type,
+ &target->abi,
+ &oformat);
+ target->os = get_zig_os_type(os_type);
+ target->is_native_os = true;
+ target->is_native_cpu = true;
+ if (target->abi == ZigLLVM_UnknownEnvironment) {
+ target->abi = target_default_abi(target->arch, target->os);
+ }
+}
+
+static Error target_parse_triple(struct ZigTarget *target, const char *zig_triple, const char *mcpu,
+ const char *dynamic_linker)
+{
+ Error err;
+
+ if (zig_triple != nullptr && strcmp(zig_triple, "native") == 0) {
+ zig_triple = nullptr;
+ }
+
+ if (zig_triple == nullptr) {
+ get_native_target(target);
+
+ if (mcpu == nullptr) {
+ target->llvm_cpu_name = ZigLLVMGetHostCPUName();
+ target->llvm_cpu_features = ZigLLVMGetNativeFeatures();
+ } else if (strcmp(mcpu, "baseline") == 0) {
+ target->is_native_os = false;
+ target->is_native_cpu = false;
+ target->llvm_cpu_name = "";
+ target->llvm_cpu_features = "";
+ } else {
+ const char *msg = "stage0 can't handle CPU/features in the target";
+ stage2_panic(msg, strlen(msg));
+ }
+ } else {
+ // first initialize all to zero
+ *target = {};
+
+ SplitIterator it = memSplit(str(zig_triple), str("-"));
+
+ Optional> opt_archsub = SplitIterator_next(&it);
+ Optional> opt_os = SplitIterator_next(&it);
+ Optional> opt_abi = SplitIterator_next(&it);
+
+ if (!opt_archsub.is_some)
+ return ErrorMissingArchitecture;
+
+ if ((err = target_parse_arch(&target->arch, (char*)opt_archsub.value.ptr, opt_archsub.value.len))) {
+ return err;
+ }
+
+ if (!opt_os.is_some)
+ return ErrorMissingOperatingSystem;
+
+ if ((err = target_parse_os(&target->os, (char*)opt_os.value.ptr, opt_os.value.len))) {
+ return err;
+ }
+
+ if (opt_abi.is_some) {
+ if ((err = target_parse_abi(&target->abi, (char*)opt_abi.value.ptr, opt_abi.value.len))) {
+ return err;
+ }
+ } else {
+ target->abi = target_default_abi(target->arch, target->os);
+ }
+
+ if (mcpu != nullptr && strcmp(mcpu, "baseline") != 0) {
+ const char *msg = "stage0 can't handle CPU/features in the target";
+ stage2_panic(msg, strlen(msg));
+ }
+ }
+
+ return ErrorNone;
+}
+
+
+static bool str_starts_with(const char *s1, const char *s2) {
+ size_t s2_len = strlen(s2);
+ if (strlen(s1) < s2_len) {
+ return false;
+ }
+ return memcmp(s1, s2, s2_len) == 0;
+}
+
+int main_exit(Stage2ProgressNode *root_progress_node, int exit_code) {
+ if (root_progress_node != nullptr) {
+ stage2_progress_end(root_progress_node);
+ }
+ return exit_code;
+}
+
+int main(int argc, char **argv) {
+ zig_stage1_os_init();
+
+ char *arg0 = argv[0];
+ Error err;
+
+ const char *in_file = nullptr;
+ const char *emit_bin_path = nullptr;
+ bool strip = false;
+ const char *out_name = nullptr;
+ bool verbose_tokenize = false;
+ bool verbose_ast = false;
+ bool verbose_ir = false;
+ bool verbose_llvm_ir = false;
+ bool verbose_cimport = false;
+ bool verbose_llvm_cpu_features = false;
+ ErrColor color = ErrColorAuto;
+ const char *dynamic_linker = nullptr;
+ bool link_libc = false;
+ bool link_libcpp = false;
+ const char *target_string = nullptr;
+ ZigStage1Pkg *cur_pkg = heap::c_allocator.create();
+ BuildMode optimize_mode = BuildModeDebug;
+ TargetSubsystem subsystem = TargetSubsystemAuto;
+ const char *override_lib_dir = nullptr;
+ const char *mcpu = nullptr;
+
+ for (int i = 1; i < argc; i += 1) {
+ char *arg = argv[i];
+
+ if (arg[0] == '-') {
+ if (strcmp(arg, "--") == 0) {
+ fprintf(stderr, "Unexpected end-of-parameter mark: %s\n", arg);
+ } else if (strcmp(arg, "-ODebug") == 0) {
+ optimize_mode = BuildModeDebug;
+ } else if (strcmp(arg, "-OReleaseFast") == 0) {
+ optimize_mode = BuildModeFastRelease;
+ } else if (strcmp(arg, "-OReleaseSafe") == 0) {
+ optimize_mode = BuildModeSafeRelease;
+ } else if (strcmp(arg, "-OReleaseSmall") == 0) {
+ optimize_mode = BuildModeSmallRelease;
+ } else if (strcmp(arg, "--help") == 0) {
+ return print_full_usage(arg0, stdout, EXIT_SUCCESS);
+ } else if (strcmp(arg, "--strip") == 0) {
+ strip = true;
+ } else if (strcmp(arg, "--verbose-tokenize") == 0) {
+ verbose_tokenize = true;
+ } else if (strcmp(arg, "--verbose-ast") == 0) {
+ verbose_ast = true;
+ } else if (strcmp(arg, "--verbose-ir") == 0) {
+ verbose_ir = true;
+ } else if (strcmp(arg, "--verbose-llvm-ir") == 0) {
+ verbose_llvm_ir = true;
+ } else if (strcmp(arg, "--verbose-cimport") == 0) {
+ verbose_cimport = true;
+ } else if (strcmp(arg, "--verbose-llvm-cpu-features") == 0) {
+ verbose_llvm_cpu_features = true;
+ } else if (arg[1] == 'l' && arg[2] != 0) {
+ // alias for --library
+ const char *l = &arg[2];
+ if (strcmp(l, "c") == 0) {
+ link_libc = true;
+ } else if (strcmp(l, "c++") == 0 || strcmp(l, "stdc++") == 0) {
+ link_libcpp = true;
+ }
+ } else if (strcmp(arg, "--pkg-begin") == 0) {
+ if (i + 2 >= argc) {
+ fprintf(stderr, "Expected 2 arguments after --pkg-begin\n");
+ return print_error_usage(arg0);
+ }
+ ZigStage1Pkg *new_cur_pkg = heap::c_allocator.create();
+ i += 1;
+ new_cur_pkg->name_ptr = argv[i];
+ new_cur_pkg->name_len = strlen(argv[i]);
+ i += 1;
+ new_cur_pkg->path_ptr = argv[i];
+ new_cur_pkg->path_len = strlen(argv[i]);
+ new_cur_pkg->parent = cur_pkg;
+ cur_pkg->children_ptr = heap::c_allocator.reallocate(cur_pkg->children_ptr,
+ cur_pkg->children_len, cur_pkg->children_len + 1);
+ cur_pkg->children_ptr[cur_pkg->children_len] = new_cur_pkg;
+ cur_pkg->children_len += 1;
+
+ cur_pkg = new_cur_pkg;
+ } else if (strcmp(arg, "--pkg-end") == 0) {
+ if (cur_pkg->parent == nullptr) {
+ fprintf(stderr, "Encountered --pkg-end with no matching --pkg-begin\n");
+ return EXIT_FAILURE;
+ }
+ cur_pkg = cur_pkg->parent;
+ } else if (str_starts_with(arg, "-mcpu=")) {
+ mcpu = arg + strlen("-mcpu=");
+ } else if (str_starts_with(arg, "-femit-bin=")) {
+ emit_bin_path = arg + strlen("-femit-bin=");
+ } else if (i + 1 >= argc) {
+ fprintf(stderr, "Expected another argument after %s\n", arg);
+ return print_error_usage(arg0);
+ } else {
+ i += 1;
+ if (strcmp(arg, "--color") == 0) {
+ if (strcmp(argv[i], "auto") == 0) {
+ color = ErrColorAuto;
+ } else if (strcmp(argv[i], "on") == 0) {
+ color = ErrColorOn;
+ } else if (strcmp(argv[i], "off") == 0) {
+ color = ErrColorOff;
+ } else {
+ fprintf(stderr, "--color options are 'auto', 'on', or 'off'\n");
+ return print_error_usage(arg0);
+ }
+ } else if (strcmp(arg, "--name") == 0) {
+ out_name = argv[i];
+ } else if (strcmp(arg, "--dynamic-linker") == 0) {
+ dynamic_linker = argv[i];
+ } else if (strcmp(arg, "--override-lib-dir") == 0) {
+ override_lib_dir = argv[i];
+ } else if (strcmp(arg, "--library") == 0 || strcmp(arg, "-l") == 0) {
+ if (strcmp(argv[i], "c") == 0) {
+ link_libc = true;
+ } else if (strcmp(argv[i], "c++") == 0 || strcmp(argv[i], "stdc++") == 0) {
+ link_libcpp = true;
+ }
+ } else if (strcmp(arg, "-target") == 0) {
+ target_string = argv[i];
+ } else if (strcmp(arg, "--subsystem") == 0) {
+ if (strcmp(argv[i], "console") == 0) {
+ subsystem = TargetSubsystemConsole;
+ } else if (strcmp(argv[i], "windows") == 0) {
+ subsystem = TargetSubsystemWindows;
+ } else if (strcmp(argv[i], "posix") == 0) {
+ subsystem = TargetSubsystemPosix;
+ } else if (strcmp(argv[i], "native") == 0) {
+ subsystem = TargetSubsystemNative;
+ } else if (strcmp(argv[i], "efi_application") == 0) {
+ subsystem = TargetSubsystemEfiApplication;
+ } else if (strcmp(argv[i], "efi_boot_service_driver") == 0) {
+ subsystem = TargetSubsystemEfiBootServiceDriver;
+ } else if (strcmp(argv[i], "efi_rom") == 0) {
+ subsystem = TargetSubsystemEfiRom;
+ } else if (strcmp(argv[i], "efi_runtime_driver") == 0) {
+ subsystem = TargetSubsystemEfiRuntimeDriver;
+ } else {
+ fprintf(stderr, "invalid: --subsystem %s\n"
+ "Options are:\n"
+ " console\n"
+ " windows\n"
+ " posix\n"
+ " native\n"
+ " efi_application\n"
+ " efi_boot_service_driver\n"
+ " efi_rom\n"
+ " efi_runtime_driver\n"
+ , argv[i]);
+ return EXIT_FAILURE;
+ }
+ } else if (strcmp(arg, "-mcpu") == 0) {
+ mcpu = argv[i];
+ } else {
+ fprintf(stderr, "Invalid argument: %s\n", arg);
+ return print_error_usage(arg0);
+ }
+ }
+ } else if (!in_file) {
+ in_file = arg;
+ } else {
+ fprintf(stderr, "Unexpected extra parameter: %s\n", arg);
+ return print_error_usage(arg0);
+ }
+ }
+
+ if (cur_pkg->parent != nullptr) {
+ fprintf(stderr, "Unmatched --pkg-begin\n");
+ return EXIT_FAILURE;
+ }
+
+ Stage2Progress *progress = stage2_progress_create();
+ Stage2ProgressNode *root_progress_node = stage2_progress_start_root(progress, "", 0, 0);
+ if (color == ErrColorOff) stage2_progress_disable_tty(progress);
+
+ ZigTarget target;
+ if ((err = target_parse_triple(&target, target_string, mcpu, dynamic_linker))) {
+ fprintf(stderr, "invalid target: %s\n", err_str(err));
+ return print_error_usage(arg0);
+ }
+
+ if (in_file == nullptr) {
+ fprintf(stderr, "missing zig file\n");
+ return print_error_usage(arg0);
+ }
+
+ if (out_name == nullptr) {
+ fprintf(stderr, "missing --name\n");
+ return print_error_usage(arg0);
+ }
+
+ ZigStage1 *stage1 = zig_stage1_create(optimize_mode,
+ nullptr, 0,
+ in_file, strlen(in_file),
+ override_lib_dir, strlen(override_lib_dir),
+ &target, false);
+
+ stage1->main_progress_node = root_progress_node;
+ stage1->root_name_ptr = out_name;
+ stage1->root_name_len = strlen(out_name);
+ stage1->strip = strip;
+ stage1->verbose_tokenize = verbose_tokenize;
+ stage1->verbose_ast = verbose_ast;
+ stage1->verbose_ir = verbose_ir;
+ stage1->verbose_llvm_ir = verbose_llvm_ir;
+ stage1->verbose_cimport = verbose_cimport;
+ stage1->verbose_llvm_cpu_features = verbose_llvm_cpu_features;
+ stage1->emit_o_ptr = emit_bin_path;
+ stage1->emit_o_len = strlen(emit_bin_path);
+ stage1->root_pkg = cur_pkg;
+ stage1->err_color = color;
+ stage1->link_libc = link_libc;
+ stage1->link_libcpp = link_libcpp;
+ stage1->subsystem = subsystem;
+ stage1->pic = true;
+
+ zig_stage1_build_object(stage1);
+
+ zig_stage1_destroy(stage1);
+
+ return main_exit(root_progress_node, EXIT_SUCCESS);
+}
+
+void stage2_panic(const char *ptr, size_t len) {
+ fwrite(ptr, 1, len, stderr);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ abort();
+}
+
+struct Stage2Progress {
+ int trash;
+};
+
+struct Stage2ProgressNode {
+ int trash;
+};
+
+Stage2Progress *stage2_progress_create(void) {
+ return nullptr;
+}
+
+void stage2_progress_destroy(Stage2Progress *progress) {}
+
+Stage2ProgressNode *stage2_progress_start_root(Stage2Progress *progress,
+ const char *name_ptr, size_t name_len, size_t estimated_total_items)
+{
+ return nullptr;
+}
+Stage2ProgressNode *stage2_progress_start(Stage2ProgressNode *node,
+ const char *name_ptr, size_t name_len, size_t estimated_total_items)
+{
+ return nullptr;
+}
+void stage2_progress_end(Stage2ProgressNode *node) {}
+void stage2_progress_complete_one(Stage2ProgressNode *node) {}
+void stage2_progress_disable_tty(Stage2Progress *progress) {}
+void stage2_progress_update_node(Stage2ProgressNode *node, size_t completed_count, size_t estimated_total_items){}
+
+const char *stage2_fetch_file(struct ZigStage1 *stage1, const char *path_ptr, size_t path_len,
+ size_t *result_len)
+{
+ Error err;
+ Buf contents_buf = BUF_INIT;
+ Buf path_buf = BUF_INIT;
+
+ buf_init_from_mem(&path_buf, path_ptr, path_len);
+ if ((err = os_fetch_file_path(&path_buf, &contents_buf))) {
+ return nullptr;
+ }
+ *result_len = buf_len(&contents_buf);
+ return buf_ptr(&contents_buf);
+}
+
+Error stage2_cimport(struct ZigStage1 *stage1, const char *c_src_ptr, size_t c_src_len,
+ const char **out_zig_path_ptr, size_t *out_zig_path_len,
+ struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len)
+{
+ const char *msg = "stage0 called stage2_cimport";
+ stage2_panic(msg, strlen(msg));
+}
+
+const char *stage2_add_link_lib(struct ZigStage1 *stage1,
+ const char *lib_name_ptr, size_t lib_name_len,
+ const char *symbol_name_ptr, size_t symbol_name_len)
+{
+ return nullptr;
+}
diff --git a/src/stage2.cpp b/src/stage2.cpp
deleted file mode 100644
index 6c010de84f..0000000000
--- a/src/stage2.cpp
+++ /dev/null
@@ -1,331 +0,0 @@
-// This file is a shim for zig1. The real implementations of these are in
-// src-self-hosted/stage1.zig
-
-#include "stage2.h"
-#include "util.hpp"
-#include "zig_llvm.h"
-#include "target.hpp"
-#include
-#include
-#include
-
-Error stage2_translate_c(struct Stage2Ast **out_ast,
- struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len,
- const char **args_begin, const char **args_end, const char *resources_path)
-{
- const char *msg = "stage0 called stage2_translate_c";
- stage2_panic(msg, strlen(msg));
-}
-
-void stage2_free_clang_errors(struct Stage2ErrorMsg *ptr, size_t len) {
- const char *msg = "stage0 called stage2_free_clang_errors";
- stage2_panic(msg, strlen(msg));
-}
-
-void stage2_zen(const char **ptr, size_t *len) {
- const char *msg = "stage0 called stage2_zen";
- stage2_panic(msg, strlen(msg));
-}
-
-int stage2_env(int argc, char** argv) {
- const char *msg = "stage0 called stage2_env";
- stage2_panic(msg, strlen(msg));
-}
-
-void stage2_attach_segfault_handler(void) { }
-
-void stage2_panic(const char *ptr, size_t len) {
- fwrite(ptr, 1, len, stderr);
- fprintf(stderr, "\n");
- fflush(stderr);
- abort();
-}
-
-void stage2_render_ast(struct Stage2Ast *ast, FILE *output_file) {
- const char *msg = "stage0 called stage2_render_ast";
- stage2_panic(msg, strlen(msg));
-}
-
-int stage2_fmt(int argc, char **argv) {
- const char *msg = "stage0 called stage2_fmt";
- stage2_panic(msg, strlen(msg));
-}
-
-stage2_DepTokenizer stage2_DepTokenizer_init(const char *input, size_t len) {
- const char *msg = "stage0 called stage2_DepTokenizer_init";
- stage2_panic(msg, strlen(msg));
-}
-
-void stage2_DepTokenizer_deinit(stage2_DepTokenizer *self) {
- const char *msg = "stage0 called stage2_DepTokenizer_deinit";
- stage2_panic(msg, strlen(msg));
-}
-
-stage2_DepNextResult stage2_DepTokenizer_next(stage2_DepTokenizer *self) {
- const char *msg = "stage0 called stage2_DepTokenizer_next";
- stage2_panic(msg, strlen(msg));
-}
-
-
-struct Stage2Progress {
- int trash;
-};
-
-struct Stage2ProgressNode {
- int trash;
-};
-
-Stage2Progress *stage2_progress_create(void) {
- return nullptr;
-}
-
-void stage2_progress_destroy(Stage2Progress *progress) {}
-
-Stage2ProgressNode *stage2_progress_start_root(Stage2Progress *progress,
- const char *name_ptr, size_t name_len, size_t estimated_total_items)
-{
- return nullptr;
-}
-Stage2ProgressNode *stage2_progress_start(Stage2ProgressNode *node,
- const char *name_ptr, size_t name_len, size_t estimated_total_items)
-{
- return nullptr;
-}
-void stage2_progress_end(Stage2ProgressNode *node) {}
-void stage2_progress_complete_one(Stage2ProgressNode *node) {}
-void stage2_progress_disable_tty(Stage2Progress *progress) {}
-void stage2_progress_update_node(Stage2ProgressNode *node, size_t completed_count, size_t estimated_total_items){}
-
-static Os get_zig_os_type(ZigLLVM_OSType os_type) {
- switch (os_type) {
- case ZigLLVM_UnknownOS:
- return OsFreestanding;
- case ZigLLVM_Ananas:
- return OsAnanas;
- case ZigLLVM_CloudABI:
- return OsCloudABI;
- case ZigLLVM_DragonFly:
- return OsDragonFly;
- case ZigLLVM_FreeBSD:
- return OsFreeBSD;
- case ZigLLVM_Fuchsia:
- return OsFuchsia;
- case ZigLLVM_IOS:
- return OsIOS;
- case ZigLLVM_KFreeBSD:
- return OsKFreeBSD;
- case ZigLLVM_Linux:
- return OsLinux;
- case ZigLLVM_Lv2:
- return OsLv2;
- case ZigLLVM_Darwin:
- case ZigLLVM_MacOSX:
- return OsMacOSX;
- case ZigLLVM_NetBSD:
- return OsNetBSD;
- case ZigLLVM_OpenBSD:
- return OsOpenBSD;
- case ZigLLVM_Solaris:
- return OsSolaris;
- case ZigLLVM_Win32:
- return OsWindows;
- case ZigLLVM_Haiku:
- return OsHaiku;
- case ZigLLVM_Minix:
- return OsMinix;
- case ZigLLVM_RTEMS:
- return OsRTEMS;
- case ZigLLVM_NaCl:
- return OsNaCl;
- case ZigLLVM_CNK:
- return OsCNK;
- case ZigLLVM_AIX:
- return OsAIX;
- case ZigLLVM_CUDA:
- return OsCUDA;
- case ZigLLVM_NVCL:
- return OsNVCL;
- case ZigLLVM_AMDHSA:
- return OsAMDHSA;
- case ZigLLVM_PS4:
- return OsPS4;
- case ZigLLVM_ELFIAMCU:
- return OsELFIAMCU;
- case ZigLLVM_TvOS:
- return OsTvOS;
- case ZigLLVM_WatchOS:
- return OsWatchOS;
- case ZigLLVM_Mesa3D:
- return OsMesa3D;
- case ZigLLVM_Contiki:
- return OsContiki;
- case ZigLLVM_AMDPAL:
- return OsAMDPAL;
- case ZigLLVM_HermitCore:
- return OsHermitCore;
- case ZigLLVM_Hurd:
- return OsHurd;
- case ZigLLVM_WASI:
- return OsWASI;
- case ZigLLVM_Emscripten:
- return OsEmscripten;
- }
- zig_unreachable();
-}
-
-static void get_native_target(ZigTarget *target) {
- // first zero initialize
- *target = {};
-
- ZigLLVM_OSType os_type;
- ZigLLVM_ObjectFormatType oformat; // ignored; based on arch/os
- ZigLLVMGetNativeTarget(
- &target->arch,
- &target->vendor,
- &os_type,
- &target->abi,
- &oformat);
- target->os = get_zig_os_type(os_type);
- target->is_native_os = true;
- target->is_native_cpu = true;
- if (target->abi == ZigLLVM_UnknownEnvironment) {
- target->abi = target_default_abi(target->arch, target->os);
- }
- if (target_is_glibc(target)) {
- target->glibc_or_darwin_version = heap::c_allocator.create();
- target_init_default_glibc_version(target);
- }
-}
-
-Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu,
- const char *dynamic_linker)
-{
- Error err;
-
- if (zig_triple != nullptr && strcmp(zig_triple, "native") == 0) {
- zig_triple = nullptr;
- }
-
- if (zig_triple == nullptr) {
- get_native_target(target);
-
- if (mcpu == nullptr) {
- target->llvm_cpu_name = ZigLLVMGetHostCPUName();
- target->llvm_cpu_features = ZigLLVMGetNativeFeatures();
- target->cache_hash = "native\n\n";
- } else if (strcmp(mcpu, "baseline") == 0) {
- target->is_native_os = false;
- target->is_native_cpu = false;
- target->llvm_cpu_name = "";
- target->llvm_cpu_features = "";
- target->cache_hash = "baseline\n\n";
- } else {
- const char *msg = "stage0 can't handle CPU/features in the target";
- stage2_panic(msg, strlen(msg));
- }
- } else {
- // first initialize all to zero
- *target = {};
-
- SplitIterator it = memSplit(str(zig_triple), str("-"));
-
- Optional> opt_archsub = SplitIterator_next(&it);
- Optional> opt_os = SplitIterator_next(&it);
- Optional> opt_abi = SplitIterator_next(&it);
-
- if (!opt_archsub.is_some)
- return ErrorMissingArchitecture;
-
- if ((err = target_parse_arch(&target->arch, (char*)opt_archsub.value.ptr, opt_archsub.value.len))) {
- return err;
- }
-
- if (!opt_os.is_some)
- return ErrorMissingOperatingSystem;
-
- if ((err = target_parse_os(&target->os, (char*)opt_os.value.ptr, opt_os.value.len))) {
- return err;
- }
-
- if (opt_abi.is_some) {
- if ((err = target_parse_abi(&target->abi, (char*)opt_abi.value.ptr, opt_abi.value.len))) {
- return err;
- }
- } else {
- target->abi = target_default_abi(target->arch, target->os);
- }
-
- if (mcpu != nullptr && strcmp(mcpu, "baseline") != 0) {
- const char *msg = "stage0 can't handle CPU/features in the target";
- stage2_panic(msg, strlen(msg));
- }
- target->cache_hash = "\n\n";
- }
-
- target->cache_hash_len = strlen(target->cache_hash);
-
- if (dynamic_linker != nullptr) {
- target->dynamic_linker = dynamic_linker;
- }
-
- return ErrorNone;
-}
-
-int stage2_cmd_targets(const char *zig_triple, const char *mcpu, const char *dynamic_linker) {
- const char *msg = "stage0 called stage2_cmd_targets";
- stage2_panic(msg, strlen(msg));
-}
-
-enum Error stage2_libc_parse(struct Stage2LibCInstallation *libc, const char *libc_file) {
- libc->include_dir = "/dummy/include";
- libc->include_dir_len = strlen(libc->include_dir);
- libc->sys_include_dir = "/dummy/sys/include";
- libc->sys_include_dir_len = strlen(libc->sys_include_dir);
- libc->crt_dir = "";
- libc->crt_dir_len = strlen(libc->crt_dir);
- libc->msvc_lib_dir = "";
- libc->msvc_lib_dir_len = strlen(libc->msvc_lib_dir);
- libc->kernel32_lib_dir = "";
- libc->kernel32_lib_dir_len = strlen(libc->kernel32_lib_dir);
- return ErrorNone;
-}
-
-enum Error stage2_libc_render(struct Stage2LibCInstallation *self, FILE *file) {
- const char *msg = "stage0 called stage2_libc_render";
- stage2_panic(msg, strlen(msg));
-}
-
-enum Error stage2_libc_find_native(struct Stage2LibCInstallation *libc) {
- const char *msg = "stage0 called stage2_libc_find_native";
- stage2_panic(msg, strlen(msg));
-}
-
-enum Error stage2_detect_native_paths(struct Stage2NativePaths *native_paths) {
- native_paths->include_dirs_ptr = nullptr;
- native_paths->include_dirs_len = 0;
-
- native_paths->lib_dirs_ptr = nullptr;
- native_paths->lib_dirs_len = 0;
-
- native_paths->rpaths_ptr = nullptr;
- native_paths->rpaths_len = 0;
-
- native_paths->warnings_ptr = nullptr;
- native_paths->warnings_len = 0;
-
- return ErrorNone;
-}
-
-void stage2_clang_arg_iterator(struct Stage2ClangArgIterator *it,
- size_t argc, char **argv)
-{
- const char *msg = "stage0 called stage2_clang_arg_iterator";
- stage2_panic(msg, strlen(msg));
-}
-
-enum Error stage2_clang_arg_next(struct Stage2ClangArgIterator *it) {
- const char *msg = "stage0 called stage2_clang_arg_next";
- stage2_panic(msg, strlen(msg));
-}
-
-const bool stage2_is_zig0 = true;
diff --git a/src/stage2.h b/src/stage2.h
deleted file mode 100644
index 38a1f77d46..0000000000
--- a/src/stage2.h
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Copyright (c) 2019 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#ifndef ZIG_STAGE2_H
-#define ZIG_STAGE2_H
-
-#include
-#include
-#include
-
-#include "zig_llvm.h"
-
-#ifdef __cplusplus
-#define ZIG_EXTERN_C extern "C"
-#else
-#define ZIG_EXTERN_C
-#endif
-
-#if defined(_MSC_VER)
-#define ZIG_ATTRIBUTE_NORETURN __declspec(noreturn)
-#else
-#define ZIG_ATTRIBUTE_NORETURN __attribute__((noreturn))
-#endif
-
-// ABI warning: the types and declarations in this file must match both those in
-// stage2.cpp and src-self-hosted/stage2.zig.
-
-// ABI warning
-enum Error {
- ErrorNone,
- ErrorNoMem,
- ErrorInvalidFormat,
- ErrorSemanticAnalyzeFail,
- ErrorAccess,
- ErrorInterrupted,
- ErrorSystemResources,
- ErrorFileNotFound,
- ErrorFileSystem,
- ErrorFileTooBig,
- ErrorDivByZero,
- ErrorOverflow,
- ErrorPathAlreadyExists,
- ErrorUnexpected,
- ErrorExactDivRemainder,
- ErrorNegativeDenominator,
- ErrorShiftedOutOneBits,
- ErrorCCompileErrors,
- ErrorEndOfFile,
- ErrorIsDir,
- ErrorNotDir,
- ErrorUnsupportedOperatingSystem,
- ErrorSharingViolation,
- ErrorPipeBusy,
- ErrorPrimitiveTypeNotFound,
- ErrorCacheUnavailable,
- ErrorPathTooLong,
- ErrorCCompilerCannotFindFile,
- ErrorNoCCompilerInstalled,
- ErrorReadingDepFile,
- ErrorInvalidDepFile,
- ErrorMissingArchitecture,
- ErrorMissingOperatingSystem,
- ErrorUnknownArchitecture,
- ErrorUnknownOperatingSystem,
- ErrorUnknownABI,
- ErrorInvalidFilename,
- ErrorDiskQuota,
- ErrorDiskSpace,
- ErrorUnexpectedWriteFailure,
- ErrorUnexpectedSeekFailure,
- ErrorUnexpectedFileTruncationFailure,
- ErrorUnimplemented,
- ErrorOperationAborted,
- ErrorBrokenPipe,
- ErrorNoSpaceLeft,
- ErrorNotLazy,
- ErrorIsAsync,
- ErrorImportOutsidePkgPath,
- ErrorUnknownCpu,
- ErrorUnknownCpuFeature,
- ErrorInvalidCpuFeatures,
- ErrorInvalidLlvmCpuFeaturesFormat,
- ErrorUnknownApplicationBinaryInterface,
- ErrorASTUnitFailure,
- ErrorBadPathName,
- ErrorSymLinkLoop,
- ErrorProcessFdQuotaExceeded,
- ErrorSystemFdQuotaExceeded,
- ErrorNoDevice,
- ErrorDeviceBusy,
- ErrorUnableToSpawnCCompiler,
- ErrorCCompilerExitCode,
- ErrorCCompilerCrashed,
- ErrorCCompilerCannotFindHeaders,
- ErrorLibCRuntimeNotFound,
- ErrorLibCStdLibHeaderNotFound,
- ErrorLibCKernel32LibNotFound,
- ErrorUnsupportedArchitecture,
- ErrorWindowsSdkNotFound,
- ErrorUnknownDynamicLinkerPath,
- ErrorTargetHasNoDynamicLinker,
- ErrorInvalidAbiVersion,
- ErrorInvalidOperatingSystemVersion,
- ErrorUnknownClangOption,
- ErrorNestedResponseFile,
- ErrorZigIsTheCCompiler,
- ErrorFileBusy,
- ErrorLocked,
-};
-
-// ABI warning
-struct Stage2ErrorMsg {
- const char *filename_ptr; // can be null
- size_t filename_len;
- const char *msg_ptr;
- size_t msg_len;
- const char *source; // valid until the ASTUnit is freed. can be null
- unsigned line; // 0 based
- unsigned column; // 0 based
- unsigned offset; // byte offset into source
-};
-
-// ABI warning
-struct Stage2Ast;
-
-// ABI warning
-ZIG_EXTERN_C enum Error stage2_translate_c(struct Stage2Ast **out_ast,
- struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len,
- const char **args_begin, const char **args_end, const char *resources_path);
-
-// ABI warning
-ZIG_EXTERN_C void stage2_free_clang_errors(struct Stage2ErrorMsg *ptr, size_t len);
-
-// ABI warning
-ZIG_EXTERN_C void stage2_render_ast(struct Stage2Ast *ast, FILE *output_file);
-
-// ABI warning
-ZIG_EXTERN_C void stage2_zen(const char **ptr, size_t *len);
-
-// ABI warning
-ZIG_EXTERN_C int stage2_env(int argc, char **argv);
-
-// ABI warning
-ZIG_EXTERN_C void stage2_attach_segfault_handler(void);
-
-// ABI warning
-ZIG_EXTERN_C ZIG_ATTRIBUTE_NORETURN void stage2_panic(const char *ptr, size_t len);
-
-// ABI warning
-ZIG_EXTERN_C int stage2_fmt(int argc, char **argv);
-
-// ABI warning
-struct stage2_DepTokenizer {
- void *handle;
-};
-
-// ABI warning
-struct stage2_DepNextResult {
- enum TypeId {
- error,
- null,
- target,
- prereq,
- };
-
- TypeId type_id;
-
- // when ent == error --> error text
- // when ent == null --> undefined
- // when ent == target --> target pathname
- // when ent == prereq --> prereq pathname
- const char *textz;
-};
-
-// ABI warning
-ZIG_EXTERN_C stage2_DepTokenizer stage2_DepTokenizer_init(const char *input, size_t len);
-
-// ABI warning
-ZIG_EXTERN_C void stage2_DepTokenizer_deinit(stage2_DepTokenizer *self);
-
-// ABI warning
-ZIG_EXTERN_C stage2_DepNextResult stage2_DepTokenizer_next(stage2_DepTokenizer *self);
-
-// ABI warning
-struct Stage2Progress;
-// ABI warning
-struct Stage2ProgressNode;
-// ABI warning
-ZIG_EXTERN_C Stage2Progress *stage2_progress_create(void);
-// ABI warning
-ZIG_EXTERN_C void stage2_progress_disable_tty(Stage2Progress *progress);
-// ABI warning
-ZIG_EXTERN_C void stage2_progress_destroy(Stage2Progress *progress);
-// ABI warning
-ZIG_EXTERN_C Stage2ProgressNode *stage2_progress_start_root(Stage2Progress *progress,
- const char *name_ptr, size_t name_len, size_t estimated_total_items);
-// ABI warning
-ZIG_EXTERN_C Stage2ProgressNode *stage2_progress_start(Stage2ProgressNode *node,
- const char *name_ptr, size_t name_len, size_t estimated_total_items);
-// ABI warning
-ZIG_EXTERN_C void stage2_progress_end(Stage2ProgressNode *node);
-// ABI warning
-ZIG_EXTERN_C void stage2_progress_complete_one(Stage2ProgressNode *node);
-// ABI warning
-ZIG_EXTERN_C void stage2_progress_update_node(Stage2ProgressNode *node,
- size_t completed_count, size_t estimated_total_items);
-
-// ABI warning
-struct Stage2LibCInstallation {
- const char *include_dir;
- size_t include_dir_len;
- const char *sys_include_dir;
- size_t sys_include_dir_len;
- const char *crt_dir;
- size_t crt_dir_len;
- const char *msvc_lib_dir;
- size_t msvc_lib_dir_len;
- const char *kernel32_lib_dir;
- size_t kernel32_lib_dir_len;
-};
-
-// ABI warning
-ZIG_EXTERN_C enum Error stage2_libc_parse(struct Stage2LibCInstallation *libc, const char *libc_file);
-// ABI warning
-ZIG_EXTERN_C enum Error stage2_libc_render(struct Stage2LibCInstallation *self, FILE *file);
-// ABI warning
-ZIG_EXTERN_C enum Error stage2_libc_find_native(struct Stage2LibCInstallation *libc);
-
-// ABI warning
-// Synchronize with target.cpp::os_list
-enum Os {
- OsFreestanding,
- OsAnanas,
- OsCloudABI,
- OsDragonFly,
- OsFreeBSD,
- OsFuchsia,
- OsIOS,
- OsKFreeBSD,
- OsLinux,
- OsLv2, // PS3
- OsMacOSX,
- OsNetBSD,
- OsOpenBSD,
- OsSolaris,
- OsWindows,
- OsHaiku,
- OsMinix,
- OsRTEMS,
- OsNaCl, // Native Client
- OsCNK, // BG/P Compute-Node Kernel
- OsAIX,
- OsCUDA, // NVIDIA CUDA
- OsNVCL, // NVIDIA OpenCL
- OsAMDHSA, // AMD HSA Runtime
- OsPS4,
- OsELFIAMCU,
- OsTvOS, // Apple tvOS
- OsWatchOS, // Apple watchOS
- OsMesa3D,
- OsContiki,
- OsAMDPAL,
- OsHermitCore,
- OsHurd,
- OsWASI,
- OsEmscripten,
- OsUefi,
- OsOther,
-};
-
-// ABI warning
-struct Stage2SemVer {
- uint32_t major;
- uint32_t minor;
- uint32_t patch;
-};
-
-// ABI warning
-struct ZigTarget {
- enum ZigLLVM_ArchType arch;
- enum ZigLLVM_VendorType vendor;
-
- enum ZigLLVM_EnvironmentType abi;
- Os os;
-
- bool is_native_os;
- bool is_native_cpu;
-
- // null means default. this is double-purposed to be darwin min version
- struct Stage2SemVer *glibc_or_darwin_version;
-
- const char *llvm_cpu_name;
- const char *llvm_cpu_features;
- const char *cpu_builtin_str;
- const char *cache_hash;
- size_t cache_hash_len;
- const char *os_builtin_str;
- const char *dynamic_linker;
- const char *standard_dynamic_linker_path;
-
- const char **llvm_cpu_features_asm_ptr;
- size_t llvm_cpu_features_asm_len;
-};
-
-// ABI warning
-ZIG_EXTERN_C enum Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu,
- const char *dynamic_linker);
-
-// ABI warning
-ZIG_EXTERN_C int stage2_cmd_targets(const char *zig_triple, const char *mcpu, const char *dynamic_linker);
-
-
-// ABI warning
-struct Stage2NativePaths {
- const char **include_dirs_ptr;
- size_t include_dirs_len;
- const char **lib_dirs_ptr;
- size_t lib_dirs_len;
- const char **rpaths_ptr;
- size_t rpaths_len;
- const char **warnings_ptr;
- size_t warnings_len;
-};
-// ABI warning
-ZIG_EXTERN_C enum Error stage2_detect_native_paths(struct Stage2NativePaths *native_paths);
-
-// ABI warning
-enum Stage2ClangArg {
- Stage2ClangArgTarget,
- Stage2ClangArgO,
- Stage2ClangArgC,
- Stage2ClangArgOther,
- Stage2ClangArgPositional,
- Stage2ClangArgL,
- Stage2ClangArgIgnore,
- Stage2ClangArgDriverPunt,
- Stage2ClangArgPIC,
- Stage2ClangArgNoPIC,
- Stage2ClangArgNoStdLib,
- Stage2ClangArgNoStdLibCpp,
- Stage2ClangArgShared,
- Stage2ClangArgRDynamic,
- Stage2ClangArgWL,
- Stage2ClangArgPreprocessOrAsm,
- Stage2ClangArgOptimize,
- Stage2ClangArgDebug,
- Stage2ClangArgSanitize,
- Stage2ClangArgLinkerScript,
- Stage2ClangArgVerboseCmds,
- Stage2ClangArgForLinker,
- Stage2ClangArgLinkerInputZ,
- Stage2ClangArgLibDir,
- Stage2ClangArgMCpu,
- Stage2ClangArgDepFile,
- Stage2ClangArgFrameworkDir,
- Stage2ClangArgFramework,
- Stage2ClangArgNoStdLibInc,
-};
-
-// ABI warning
-struct Stage2ClangArgIterator {
- bool has_next;
- enum Stage2ClangArg kind;
- const char *only_arg;
- const char *second_arg;
- const char **other_args_ptr;
- size_t other_args_len;
- const char **argv_ptr;
- size_t argv_len;
- size_t next_index;
- size_t root_args;
-};
-
-// ABI warning
-ZIG_EXTERN_C void stage2_clang_arg_iterator(struct Stage2ClangArgIterator *it,
- size_t argc, char **argv);
-
-// ABI warning
-ZIG_EXTERN_C enum Error stage2_clang_arg_next(struct Stage2ClangArgIterator *it);
-
-// ABI warning
-ZIG_EXTERN_C const bool stage2_is_zig0;
-
-#endif
diff --git a/src/target.zig b/src/target.zig
new file mode 100644
index 0000000000..fc0c7a0745
--- /dev/null
+++ b/src/target.zig
@@ -0,0 +1,342 @@
+const std = @import("std");
+const llvm = @import("llvm.zig");
+
+pub const ArchOsAbi = struct {
+ arch: std.Target.Cpu.Arch,
+ os: std.Target.Os.Tag,
+ abi: std.Target.Abi,
+};
+
+pub const available_libcs = [_]ArchOsAbi{
+ .{ .arch = .aarch64_be, .os = .linux, .abi = .gnu },
+ .{ .arch = .aarch64_be, .os = .linux, .abi = .musl },
+ .{ .arch = .aarch64_be, .os = .windows, .abi = .gnu },
+ .{ .arch = .aarch64, .os = .linux, .abi = .gnu },
+ .{ .arch = .aarch64, .os = .linux, .abi = .musl },
+ .{ .arch = .aarch64, .os = .windows, .abi = .gnu },
+ .{ .arch = .armeb, .os = .linux, .abi = .gnueabi },
+ .{ .arch = .armeb, .os = .linux, .abi = .gnueabihf },
+ .{ .arch = .armeb, .os = .linux, .abi = .musleabi },
+ .{ .arch = .armeb, .os = .linux, .abi = .musleabihf },
+ .{ .arch = .armeb, .os = .windows, .abi = .gnu },
+ .{ .arch = .arm, .os = .linux, .abi = .gnueabi },
+ .{ .arch = .arm, .os = .linux, .abi = .gnueabihf },
+ .{ .arch = .arm, .os = .linux, .abi = .musleabi },
+ .{ .arch = .arm, .os = .linux, .abi = .musleabihf },
+ .{ .arch = .arm, .os = .windows, .abi = .gnu },
+ .{ .arch = .i386, .os = .linux, .abi = .gnu },
+ .{ .arch = .i386, .os = .linux, .abi = .musl },
+ .{ .arch = .i386, .os = .windows, .abi = .gnu },
+ .{ .arch = .mips64el, .os = .linux, .abi = .gnuabi64 },
+ .{ .arch = .mips64el, .os = .linux, .abi = .gnuabin32 },
+ .{ .arch = .mips64el, .os = .linux, .abi = .musl },
+ .{ .arch = .mips64, .os = .linux, .abi = .gnuabi64 },
+ .{ .arch = .mips64, .os = .linux, .abi = .gnuabin32 },
+ .{ .arch = .mips64, .os = .linux, .abi = .musl },
+ .{ .arch = .mipsel, .os = .linux, .abi = .gnu },
+ .{ .arch = .mipsel, .os = .linux, .abi = .musl },
+ .{ .arch = .mips, .os = .linux, .abi = .gnu },
+ .{ .arch = .mips, .os = .linux, .abi = .musl },
+ .{ .arch = .powerpc64le, .os = .linux, .abi = .gnu },
+ .{ .arch = .powerpc64le, .os = .linux, .abi = .musl },
+ .{ .arch = .powerpc64, .os = .linux, .abi = .gnu },
+ .{ .arch = .powerpc64, .os = .linux, .abi = .musl },
+ .{ .arch = .powerpc, .os = .linux, .abi = .gnu },
+ .{ .arch = .powerpc, .os = .linux, .abi = .musl },
+ .{ .arch = .riscv64, .os = .linux, .abi = .gnu },
+ .{ .arch = .riscv64, .os = .linux, .abi = .musl },
+ .{ .arch = .s390x, .os = .linux, .abi = .gnu },
+ .{ .arch = .s390x, .os = .linux, .abi = .musl },
+ .{ .arch = .sparc, .os = .linux, .abi = .gnu },
+ .{ .arch = .sparcv9, .os = .linux, .abi = .gnu },
+ .{ .arch = .wasm32, .os = .freestanding, .abi = .musl },
+ .{ .arch = .x86_64, .os = .linux, .abi = .gnu },
+ .{ .arch = .x86_64, .os = .linux, .abi = .gnux32 },
+ .{ .arch = .x86_64, .os = .linux, .abi = .musl },
+ .{ .arch = .x86_64, .os = .windows, .abi = .gnu },
+};
+
+pub fn libCGenericName(target: std.Target) [:0]const u8 {
+ if (target.os.tag == .windows)
+ return "mingw";
+ switch (target.abi) {
+ .gnu,
+ .gnuabin32,
+ .gnuabi64,
+ .gnueabi,
+ .gnueabihf,
+ .gnux32,
+ => return "glibc",
+ .musl,
+ .musleabi,
+ .musleabihf,
+ .none,
+ => return "musl",
+ .code16,
+ .eabi,
+ .eabihf,
+ .android,
+ .msvc,
+ .itanium,
+ .cygnus,
+ .coreclr,
+ .simulator,
+ .macabi,
+ => unreachable,
+ }
+}
+
+pub fn archMuslName(arch: std.Target.Cpu.Arch) [:0]const u8 {
+ switch (arch) {
+ .aarch64, .aarch64_be => return "aarch64",
+ .arm, .armeb => return "arm",
+ .mips, .mipsel => return "mips",
+ .mips64el, .mips64 => return "mips64",
+ .powerpc => return "powerpc",
+ .powerpc64, .powerpc64le => return "powerpc64",
+ .s390x => return "s390x",
+ .i386 => return "i386",
+ .x86_64 => return "x86_64",
+ .riscv64 => return "riscv64",
+ else => unreachable,
+ }
+}
+
+pub fn canBuildLibC(target: std.Target) bool {
+ for (available_libcs) |libc| {
+ if (target.cpu.arch == libc.arch and target.os.tag == libc.os and target.abi == libc.abi) {
+ return true;
+ }
+ }
+ return false;
+}
+
+pub fn cannotDynamicLink(target: std.Target) bool {
+ return switch (target.os.tag) {
+ .freestanding, .other => true,
+ else => false,
+ };
+}
+
+/// On Darwin, we always link libSystem which contains libc.
+/// Similarly on FreeBSD and NetBSD we always link system libc
+/// since this is the stable syscall interface.
+pub fn osRequiresLibC(target: std.Target) bool {
+ return switch (target.os.tag) {
+ .freebsd, .netbsd, .dragonfly, .macosx, .ios, .watchos, .tvos => true,
+ else => false,
+ };
+}
+
+pub fn libcNeedsLibUnwind(target: std.Target) bool {
+ return switch (target.os.tag) {
+ .windows,
+ .macosx,
+ .ios,
+ .watchos,
+ .tvos,
+ .freestanding,
+ => false,
+
+ else => true,
+ };
+}
+
+pub fn requiresPIE(target: std.Target) bool {
+ return target.isAndroid() or target.isDarwin();
+}
+
+/// This function returns whether non-pic code is completely invalid on the given target.
+pub fn requiresPIC(target: std.Target, linking_libc: bool) bool {
+ return target.isAndroid() or
+ target.os.tag == .windows or target.os.tag == .uefi or
+ osRequiresLibC(target) or
+ (linking_libc and target.isGnuLibC());
+}
+
+/// This is not whether the target supports Position Independent Code, but whether the -fPIC
+/// C compiler argument is valid to Clang.
+pub fn supports_fpic(target: std.Target) bool {
+ return target.os.tag != .windows;
+}
+
+pub fn libc_needs_crti_crtn(target: std.Target) bool {
+ return !(target.cpu.arch.isRISCV() or target.isAndroid());
+}
+
+pub fn isSingleThreaded(target: std.Target) bool {
+ return target.isWasm();
+}
+
+/// Valgrind supports more, but Zig does not support them yet.
+pub fn hasValgrindSupport(target: std.Target) bool {
+ switch (target.cpu.arch) {
+ .x86_64 => {
+ return target.os.tag == .linux or target.isDarwin() or target.os.tag == .solaris or
+ (target.os.tag == .windows and target.abi != .msvc);
+ },
+ else => return false,
+ }
+}
+
+pub fn supportsStackProbing(target: std.Target) bool {
+ return target.os.tag != .windows and target.os.tag != .uefi and
+ (target.cpu.arch == .i386 or target.cpu.arch == .x86_64);
+}
+
+pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType {
+ return switch (os_tag) {
+ .freestanding, .other => .UnknownOS,
+ .windows, .uefi => .Win32,
+ .ananas => .Ananas,
+ .cloudabi => .CloudABI,
+ .dragonfly => .DragonFly,
+ .freebsd => .FreeBSD,
+ .fuchsia => .Fuchsia,
+ .ios => .IOS,
+ .kfreebsd => .KFreeBSD,
+ .linux => .Linux,
+ .lv2 => .Lv2,
+ .macosx => .MacOSX,
+ .netbsd => .NetBSD,
+ .openbsd => .OpenBSD,
+ .solaris => .Solaris,
+ .haiku => .Haiku,
+ .minix => .Minix,
+ .rtems => .RTEMS,
+ .nacl => .NaCl,
+ .cnk => .CNK,
+ .aix => .AIX,
+ .cuda => .CUDA,
+ .nvcl => .NVCL,
+ .amdhsa => .AMDHSA,
+ .ps4 => .PS4,
+ .elfiamcu => .ELFIAMCU,
+ .tvos => .TvOS,
+ .watchos => .WatchOS,
+ .mesa3d => .Mesa3D,
+ .contiki => .Contiki,
+ .amdpal => .AMDPAL,
+ .hermit => .HermitCore,
+ .hurd => .Hurd,
+ .wasi => .WASI,
+ .emscripten => .Emscripten,
+ };
+}
+
+pub fn archToLLVM(arch_tag: std.Target.Cpu.Arch) llvm.ArchType {
+ return switch (arch_tag) {
+ .arm => .arm,
+ .armeb => .armeb,
+ .aarch64 => .aarch64,
+ .aarch64_be => .aarch64_be,
+ .aarch64_32 => .aarch64_32,
+ .arc => .arc,
+ .avr => .avr,
+ .bpfel => .bpfel,
+ .bpfeb => .bpfeb,
+ .hexagon => .hexagon,
+ .mips => .mips,
+ .mipsel => .mipsel,
+ .mips64 => .mips64,
+ .mips64el => .mips64el,
+ .msp430 => .msp430,
+ .powerpc => .ppc,
+ .powerpc64 => .ppc64,
+ .powerpc64le => .ppc64le,
+ .r600 => .r600,
+ .amdgcn => .amdgcn,
+ .riscv32 => .riscv32,
+ .riscv64 => .riscv64,
+ .sparc => .sparc,
+ .sparcv9 => .sparcv9,
+ .sparcel => .sparcel,
+ .s390x => .systemz,
+ .tce => .tce,
+ .tcele => .tcele,
+ .thumb => .thumb,
+ .thumbeb => .thumbeb,
+ .i386 => .x86,
+ .x86_64 => .x86_64,
+ .xcore => .xcore,
+ .nvptx => .nvptx,
+ .nvptx64 => .nvptx64,
+ .le32 => .le32,
+ .le64 => .le64,
+ .amdil => .amdil,
+ .amdil64 => .amdil64,
+ .hsail => .hsail,
+ .hsail64 => .hsail64,
+ .spir => .spir,
+ .spir64 => .spir64,
+ .kalimba => .kalimba,
+ .shave => .shave,
+ .lanai => .lanai,
+ .wasm32 => .wasm32,
+ .wasm64 => .wasm64,
+ .renderscript32 => .renderscript32,
+ .renderscript64 => .renderscript64,
+ .ve => .ve,
+ .spu_2 => .UnknownArch,
+ };
+}
+
+fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool {
+ if (ignore_case) {
+ return std.ascii.eqlIgnoreCase(a, b);
+ } else {
+ return std.mem.eql(u8, a, b);
+ }
+}
+
+pub fn is_libc_lib_name(target: std.Target, name: []const u8) bool {
+ const ignore_case = target.os.tag.isDarwin() or target.os.tag == .windows;
+
+ if (eqlIgnoreCase(ignore_case, name, "c"))
+ return true;
+
+ if (target.isMinGW()) {
+ if (eqlIgnoreCase(ignore_case, name, "m"))
+ return true;
+
+ return false;
+ }
+
+ if (target.abi.isGnu() or target.abi.isMusl() or target.os.tag.isDarwin()) {
+ if (eqlIgnoreCase(ignore_case, name, "m"))
+ return true;
+ if (eqlIgnoreCase(ignore_case, name, "rt"))
+ return true;
+ if (eqlIgnoreCase(ignore_case, name, "pthread"))
+ return true;
+ if (eqlIgnoreCase(ignore_case, name, "crypt"))
+ return true;
+ if (eqlIgnoreCase(ignore_case, name, "util"))
+ return true;
+ if (eqlIgnoreCase(ignore_case, name, "xnet"))
+ return true;
+ if (eqlIgnoreCase(ignore_case, name, "resolv"))
+ return true;
+ if (eqlIgnoreCase(ignore_case, name, "dl"))
+ return true;
+ if (eqlIgnoreCase(ignore_case, name, "util"))
+ return true;
+ }
+
+ if (target.os.tag.isDarwin() and eqlIgnoreCase(ignore_case, name, "System"))
+ return true;
+
+ return false;
+}
+
+pub fn is_libcpp_lib_name(target: std.Target, name: []const u8) bool {
+ const ignore_case = target.os.tag.isDarwin() or target.os.tag == .windows;
+
+ return eqlIgnoreCase(ignore_case, name, "c++") or
+ eqlIgnoreCase(ignore_case, name, "stdc++") or
+ eqlIgnoreCase(ignore_case, name, "c++abi");
+}
+
+pub fn hasDebugInfo(target: std.Target) bool {
+ return !target.cpu.arch.isWasm();
+}
diff --git a/src-self-hosted/test.zig b/src/test.zig
similarity index 89%
rename from src-self-hosted/test.zig
rename to src/test.zig
index aef48e198b..8ad11efa9c 100644
--- a/src-self-hosted/test.zig
+++ b/src/test.zig
@@ -1,9 +1,10 @@
const std = @import("std");
const link = @import("link.zig");
-const Module = @import("Module.zig");
+const Compilation = @import("Compilation.zig");
const Allocator = std.mem.Allocator;
const zir = @import("zir.zig");
const Package = @import("Package.zig");
+const introspect = @import("introspect.zig");
const build_options = @import("build_options");
const enable_qemu: bool = build_options.enable_qemu;
const enable_wine: bool = build_options.enable_wine;
@@ -60,7 +61,7 @@ pub const TestContext = struct {
ZIR,
};
- /// A Case consists of a set of *updates*. The same Module is used for each
+ /// A Case consists of a set of *updates*. The same Compilation is used for each
/// update, so each update's source is treated as a single file being
/// updated by the test harness and incrementally compiled.
pub const Case = struct {
@@ -406,6 +407,17 @@ pub const TestContext = struct {
const root_node = try progress.start("tests", self.cases.items.len);
defer root_node.end();
+ var zig_lib_directory = try introspect.findZigLibDir(std.testing.allocator);
+ defer zig_lib_directory.handle.close();
+ defer std.testing.allocator.free(zig_lib_directory.path.?);
+
+ const random_seed = blk: {
+ var random_seed: u64 = undefined;
+ try std.crypto.randomBytes(std.mem.asBytes(&random_seed));
+ break :blk random_seed;
+ };
+ var default_prng = std.rand.DefaultPrng.init(random_seed);
+
for (self.cases.items) |case| {
var prg_node = root_node.start(case.name, case.updates.items.len);
prg_node.activate();
@@ -416,11 +428,18 @@ pub const TestContext = struct {
progress.initial_delay_ns = 0;
progress.refresh_rate_ns = 0;
- try self.runOneCase(std.testing.allocator, &prg_node, case);
+ try self.runOneCase(std.testing.allocator, &prg_node, case, zig_lib_directory, &default_prng.random);
}
}
- fn runOneCase(self: *TestContext, allocator: *Allocator, root_node: *std.Progress.Node, case: Case) !void {
+ fn runOneCase(
+ self: *TestContext,
+ allocator: *Allocator,
+ root_node: *std.Progress.Node,
+ case: Case,
+ zig_lib_directory: Compilation.Directory,
+ rand: *std.rand.Random,
+ ) !void {
const target_info = try std.zig.system.NativeTargetInfo.detect(allocator, case.target);
const target = target_info.target;
@@ -431,13 +450,45 @@ pub const TestContext = struct {
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
- const tmp_src_path = if (case.extension == .Zig) "test_case.zig" else if (case.extension == .ZIR) "test_case.zir" else unreachable;
- const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
- defer root_pkg.destroy();
+ var cache_dir = try tmp.dir.makeOpenPath("zig-cache", .{});
+ defer cache_dir.close();
+ const bogus_path = "bogus"; // TODO this will need to be fixed before we can test LLVM extensions
+ const zig_cache_directory: Compilation.Directory = .{
+ .handle = cache_dir,
+ .path = try std.fs.path.join(arena, &[_][]const u8{ bogus_path, "zig-cache" }),
+ };
- const bin_name = try std.zig.binNameAlloc(arena, "test_case", target, case.output_mode, null);
+ const tmp_src_path = switch (case.extension) {
+ .Zig => "test_case.zig",
+ .ZIR => "test_case.zir",
+ };
- var module = try Module.init(allocator, .{
+ var root_pkg: Package = .{
+ .root_src_directory = .{ .path = bogus_path, .handle = tmp.dir },
+ .root_src_path = tmp_src_path,
+ };
+
+ const ofmt: ?std.builtin.ObjectFormat = if (case.cbe) .c else null;
+ const bin_name = try std.zig.binNameAlloc(arena, .{
+ .root_name = "test_case",
+ .target = target,
+ .output_mode = case.output_mode,
+ .object_format = ofmt,
+ });
+
+ const emit_directory: Compilation.Directory = .{
+ .path = bogus_path,
+ .handle = tmp.dir,
+ };
+ const emit_bin: Compilation.EmitLoc = .{
+ .directory = emit_directory,
+ .basename = bin_name,
+ };
+ const comp = try Compilation.create(allocator, .{
+ .local_cache_directory = zig_cache_directory,
+ .global_cache_directory = zig_cache_directory,
+ .zig_lib_directory = zig_lib_directory,
+ .rand = rand,
.root_name = "test_case",
.target = target,
// TODO: support tests for object file building, and library builds
@@ -446,13 +497,13 @@ pub const TestContext = struct {
.output_mode = case.output_mode,
// TODO: support testing optimizations
.optimize_mode = .Debug,
- .bin_file_dir = tmp.dir,
- .bin_file_path = bin_name,
- .root_pkg = root_pkg,
+ .emit_bin = emit_bin,
+ .root_pkg = &root_pkg,
.keep_source_files_loaded = true,
- .object_format = if (case.cbe) .c else null,
+ .object_format = ofmt,
+ .is_native_os = case.target.isNativeOs(),
});
- defer module.deinit();
+ defer comp.destroy();
for (case.updates.items) |update, update_index| {
var update_node = root_node.start("update", 3);
@@ -466,20 +517,20 @@ pub const TestContext = struct {
var module_node = update_node.start("parse/analysis/codegen", null);
module_node.activate();
- try module.makeBinFileWritable();
- try module.update();
+ try comp.makeBinFileWritable();
+ try comp.update();
module_node.end();
if (update.case != .Error) {
- var all_errors = try module.getAllErrorsAlloc();
+ var all_errors = try comp.getAllErrorsAlloc();
defer all_errors.deinit(allocator);
if (all_errors.list.len != 0) {
- std.debug.print("\nErrors occurred updating the module:\n================\n", .{});
+ std.debug.print("\nErrors occurred updating the compilation:\n================\n", .{});
for (all_errors.list) |err| {
std.debug.print(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, err.msg });
}
if (case.cbe) {
- const C = module.bin_file.cast(link.File.C).?;
+ const C = comp.bin_file.cast(link.File.C).?;
std.debug.print("Generated C: \n===============\n{}\n\n===========\n\n", .{C.main.items});
}
std.debug.print("Test failed.\n", .{});
@@ -510,7 +561,7 @@ pub const TestContext = struct {
update_node.estimated_total_items = 5;
var emit_node = update_node.start("emit", null);
emit_node.activate();
- var new_zir_module = try zir.emit(allocator, module);
+ var new_zir_module = try zir.emit(allocator, comp.bin_file.options.module.?);
defer new_zir_module.deinit(allocator);
emit_node.end();
@@ -545,7 +596,7 @@ pub const TestContext = struct {
for (handled_errors) |*h| {
h.* = false;
}
- var all_errors = try module.getAllErrorsAlloc();
+ var all_errors = try comp.getAllErrorsAlloc();
defer all_errors.deinit(allocator);
for (all_errors.list) |a| {
for (e) |ex, i| {
@@ -627,7 +678,7 @@ pub const TestContext = struct {
},
}
- try module.makeBinFileExecutable();
+ try comp.makeBinFileExecutable();
break :x try std.ChildProcess.exec(.{
.allocator = allocator,
@@ -693,23 +744,23 @@ pub const TestContext = struct {
}
var interpreter = spu.Interpreter(struct {
- RAM: [0x10000]u8 = undefined,
+ RAM: [0x10000]u8 = undefined,
- pub fn read8(bus: @This(), addr: u16) u8 {
- return bus.RAM[addr];
- }
- pub fn read16(bus: @This(), addr: u16) u16 {
- return std.mem.readIntLittle(u16, bus.RAM[addr..][0..2]);
- }
+ pub fn read8(bus: @This(), addr: u16) u8 {
+ return bus.RAM[addr];
+ }
+ pub fn read16(bus: @This(), addr: u16) u16 {
+ return std.mem.readIntLittle(u16, bus.RAM[addr..][0..2]);
+ }
- pub fn write8(bus: *@This(), addr: u16, val: u8) void {
- bus.RAM[addr] = val;
- }
+ pub fn write8(bus: *@This(), addr: u16, val: u8) void {
+ bus.RAM[addr] = val;
+ }
- pub fn write16(bus: *@This(), addr: u16, val: u16) void {
- std.mem.writeIntLittle(u16, bus.RAM[addr..][0..2], val);
- }
- }){
+ pub fn write16(bus: *@This(), addr: u16, val: u16) void {
+ std.mem.writeIntLittle(u16, bus.RAM[addr..][0..2], val);
+ }
+ }){
.bus = .{},
};
diff --git a/src-self-hosted/tracy.zig b/src/tracy.zig
similarity index 100%
rename from src-self-hosted/tracy.zig
rename to src/tracy.zig
diff --git a/src-self-hosted/translate_c.zig b/src/translate_c.zig
similarity index 99%
rename from src-self-hosted/translate_c.zig
rename to src/translate_c.zig
index a5619d56fe..66b5752930 100644
--- a/src-self-hosted/translate_c.zig
+++ b/src/translate_c.zig
@@ -1,5 +1,5 @@
-// This is the userland implementation of translate-c which is used by both stage1
-// and stage2.
+//! This is the userland implementation of translate-c which is used by both stage1
+//! and stage2.
const std = @import("std");
const assert = std.debug.assert;
diff --git a/src-self-hosted/type.zig b/src/type.zig
similarity index 100%
rename from src-self-hosted/type.zig
rename to src/type.zig
diff --git a/src-self-hosted/value.zig b/src/value.zig
similarity index 100%
rename from src-self-hosted/value.zig
rename to src/value.zig
diff --git a/src/windows_sdk.h b/src/windows_sdk.h
index e887209559..a4707b3de0 100644
--- a/src/windows_sdk.h
+++ b/src/windows_sdk.h
@@ -16,7 +16,7 @@
#include
-// ABI warning - src-self-hosted/windows_sdk.zig
+// ABI warning - src/windows_sdk.zig
struct ZigWindowsSDK {
const char *path10_ptr;
size_t path10_len;
@@ -34,7 +34,7 @@ struct ZigWindowsSDK {
size_t msvc_lib_dir_len;
};
-// ABI warning - src-self-hosted/windows_sdk.zig
+// ABI warning - src/windows_sdk.zig
enum ZigFindWindowsSdkError {
ZigFindWindowsSdkErrorNone,
ZigFindWindowsSdkErrorOutOfMemory,
@@ -42,10 +42,10 @@ enum ZigFindWindowsSdkError {
ZigFindWindowsSdkErrorPathTooLong,
};
-// ABI warning - src-self-hosted/windows_sdk.zig
+// ABI warning - src/windows_sdk.zig
ZIG_EXTERN_C enum ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk);
-// ABI warning - src-self-hosted/windows_sdk.zig
+// ABI warning - src/windows_sdk.zig
ZIG_EXTERN_C void zig_free_windows_sdk(struct ZigWindowsSDK *sdk);
#endif
diff --git a/src-self-hosted/windows_sdk.zig b/src/windows_sdk.zig
similarity index 100%
rename from src-self-hosted/windows_sdk.zig
rename to src/windows_sdk.zig
diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp
index 972140e00f..c2eb4f1524 100644
--- a/src/zig_clang.cpp
+++ b/src/zig_clang.cpp
@@ -13,7 +13,6 @@
* 3. Prevent C++ from infecting the rest of the project.
*/
#include "zig_clang.h"
-#include "list.hpp"
#if __GNUC__ >= 8
#pragma GCC diagnostic push
@@ -2295,7 +2294,7 @@ ZigClangASTUnit *ZigClangLoadFromCommandLine(const char **args_begin, const char
// Take ownership of the err_unit ASTUnit object so that it won't be
// free'd when we return, invalidating the error message pointers
clang::ASTUnit *unit = ast_unit ? ast_unit : err_unit.release();
- ZigList errors = {};
+ Stage2ErrorMsg *errors = nullptr;
for (clang::ASTUnit::stored_diag_iterator it = unit->stored_diag_begin(),
it_end = unit->stored_diag_end(); it != it_end; ++it)
@@ -2313,7 +2312,10 @@ ZigClangASTUnit *ZigClangLoadFromCommandLine(const char **args_begin, const char
llvm::StringRef msg_str_ref = it->getMessage();
- Stage2ErrorMsg *msg = errors.add_one();
+ *errors_len += 1;
+ errors = reinterpret_cast(realloc(errors, sizeof(Stage2ErrorMsg) * *errors_len));
+ if (errors == nullptr) abort();
+ Stage2ErrorMsg *msg = &errors[*errors_len - 1];
memset(msg, 0, sizeof(*msg));
msg->msg_ptr = (const char *)msg_str_ref.bytes_begin();
@@ -2351,8 +2353,7 @@ ZigClangASTUnit *ZigClangLoadFromCommandLine(const char **args_begin, const char
}
}
- *errors_ptr = errors.items;
- *errors_len = errors.length;
+ *errors_ptr = errors;
return nullptr;
}
diff --git a/src/zig_clang.h b/src/zig_clang.h
index 39a93b3d3e..1fbf7359de 100644
--- a/src/zig_clang.h
+++ b/src/zig_clang.h
@@ -14,7 +14,7 @@
// ATTENTION: If you modify this file, be sure to update the corresponding
// extern function declarations in the self-hosted compiler file
-// src-self-hosted/clang.zig.
+// src/clang.zig.
struct ZigClangSourceLocation {
unsigned ID;
diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp
index 5a66d0ba6a..8c3d5ab644 100644
--- a/src/zig_llvm.cpp
+++ b/src/zig_llvm.cpp
@@ -930,7 +930,7 @@ class MyOStream: public raw_ostream {
};
bool ZigLLVMWriteImportLibrary(const char *def_path, const ZigLLVM_ArchType arch,
- const char *output_lib_path, const bool kill_at)
+ const char *output_lib_path, bool kill_at)
{
COFF::MachineTypes machine = COFF::IMAGE_FILE_MACHINE_UNKNOWN;
diff --git a/src/zig_llvm.h b/src/zig_llvm.h
index f07684f2a4..007d8afc1f 100644
--- a/src/zig_llvm.h
+++ b/src/zig_llvm.h
@@ -60,7 +60,7 @@ enum ZigLLVMABIType {
ZIG_EXTERN_C LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Triple,
const char *CPU, const char *Features, LLVMCodeGenOptLevel Level, LLVMRelocMode Reloc,
- LLVMCodeModel CodeModel, bool function_sections, ZigLLVMABIType float_abi, const char *abi_name);
+ LLVMCodeModel CodeModel, bool function_sections, enum ZigLLVMABIType float_abi, const char *abi_name);
ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref);
@@ -500,8 +500,8 @@ ZIG_EXTERN_C bool ZigLLDLink(enum ZigLLVM_ObjectFormatType oformat, const char *
ZIG_EXTERN_C bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count,
enum ZigLLVM_OSType os_type);
-bool ZigLLVMWriteImportLibrary(const char *def_path, const enum ZigLLVM_ArchType arch,
- const char *output_lib_path, const bool kill_at);
+ZIG_EXTERN_C bool ZigLLVMWriteImportLibrary(const char *def_path, const enum ZigLLVM_ArchType arch,
+ const char *output_lib_path, bool kill_at);
ZIG_EXTERN_C void ZigLLVMGetNativeTarget(enum ZigLLVM_ArchType *arch_type,
enum ZigLLVM_VendorType *vendor_type, enum ZigLLVM_OSType *os_type, enum ZigLLVM_EnvironmentType *environ_type,
diff --git a/src-self-hosted/zir.zig b/src/zir.zig
similarity index 99%
rename from src-self-hosted/zir.zig
rename to src/zir.zig
index b6d7fab4c5..7e723fc674 100644
--- a/src-self-hosted/zir.zig
+++ b/src/zir.zig
@@ -1700,12 +1700,12 @@ const Parser = struct {
}
};
-pub fn emit(allocator: *Allocator, old_module: IrModule) !Module {
+pub fn emit(allocator: *Allocator, old_module: *IrModule) !Module {
var ctx: EmitZIR = .{
.allocator = allocator,
.decls = .{},
.arena = std.heap.ArenaAllocator.init(allocator),
- .old_module = &old_module,
+ .old_module = old_module,
.next_auto_name = 0,
.names = std.StringArrayHashMap(void).init(allocator),
.primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator),
@@ -2539,7 +2539,7 @@ const EmitZIR = struct {
return self.emitUnnamedDecl(&fntype_inst.base);
},
.Int => {
- const info = ty.intInfo(self.old_module.target());
+ const info = ty.intInfo(self.old_module.getTarget());
const signed = try self.emitPrimitive(src, if (info.signed) .@"true" else .@"false");
const bits_payload = try self.arena.allocator.create(Value.Payload.Int_u64);
bits_payload.* = .{ .int = info.bits };
diff --git a/src-self-hosted/zir_sema.zig b/src/zir_sema.zig
similarity index 99%
rename from src-self-hosted/zir_sema.zig
rename to src/zir_sema.zig
index c99da39c04..10543d2ee6 100644
--- a/src-self-hosted/zir_sema.zig
+++ b/src/zir_sema.zig
@@ -199,10 +199,10 @@ pub fn analyzeZirDecl(mod: *Module, decl: *Decl, src_decl: *zir.Decl) InnerError
// We don't fully codegen the decl until later, but we do need to reserve a global
// offset table index for it. This allows us to codegen decls out of dependency order,
// increasing how many computations can be done in parallel.
- try mod.bin_file.allocateDeclIndexes(decl);
- try mod.work_queue.writeItem(.{ .codegen_decl = decl });
+ try mod.comp.bin_file.allocateDeclIndexes(decl);
+ try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
} else if (prev_type_has_bits) {
- mod.bin_file.freeDecl(decl);
+ mod.comp.bin_file.freeDecl(decl);
}
return type_changed;
diff --git a/test/cli.zig b/test/cli.zig
index 77d79ed98e..7a0a7d6459 100644
--- a/test/cli.zig
+++ b/test/cli.zig
@@ -58,7 +58,7 @@ fn printCmd(cwd: []const u8, argv: []const []const u8) void {
std.debug.warn("\n", .{});
}
-fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult {
+fn exec(cwd: []const u8, expect_0: bool, argv: []const []const u8) !ChildProcess.ExecResult {
const max_output_size = 100 * 1024;
const result = ChildProcess.exec(.{
.allocator = a,
@@ -72,7 +72,7 @@ fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult {
};
switch (result.term) {
.Exited => |code| {
- if (code != 0) {
+ if ((code != 0) == expect_0) {
std.debug.warn("The following command exited with error code {}:\n", .{code});
printCmd(cwd, argv);
std.debug.warn("stderr:\n{}\n", .{result.stderr});
@@ -90,15 +90,15 @@ fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult {
}
fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void {
- _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-lib" });
- const test_result = try exec(dir_path, &[_][]const u8{ zig_exe, "build", "test" });
+ _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-lib" });
+ const test_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "test" });
testing.expect(std.mem.endsWith(u8, test_result.stderr, "All 1 tests passed.\n"));
}
fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void {
- _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" });
- const run_result = try exec(dir_path, &[_][]const u8{ zig_exe, "build", "run" });
- testing.expect(std.mem.eql(u8, run_result.stderr, "All your codebase are belong to us.\n"));
+ _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" });
+ const run_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "run" });
+ testing.expect(std.mem.eql(u8, run_result.stderr, "info: All your codebase are belong to us.\n"));
}
fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void {
@@ -118,17 +118,20 @@ fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void {
\\}
);
- const args = [_][]const u8{
+ var args = std.ArrayList([]const u8).init(a);
+ try args.appendSlice(&[_][]const u8{
zig_exe, "build-obj",
"--cache-dir", dir_path,
"--name", "example",
- "--output-dir", dir_path,
- "--emit", "asm",
- "-mllvm", "--x86-asm-syntax=intel",
- "--strip", "--release-fast",
- example_zig_path, "--disable-gen-h",
- };
- _ = try exec(dir_path, &args);
+ "-fno-emit-bin", "-fno-emit-h",
+ "--strip", "-OReleaseFast",
+ example_zig_path,
+ });
+
+ const emit_asm_arg = try std.fmt.allocPrint(a, "-femit-asm={s}", .{example_s_path});
+ try args.append(emit_asm_arg);
+
+ _ = try exec(dir_path, true, args.items);
const out_asm = try std.fs.cwd().readFileAlloc(a, example_s_path, std.math.maxInt(usize));
testing.expect(std.mem.indexOf(u8, out_asm, "square:") != null);
@@ -137,23 +140,25 @@ fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void {
}
fn testMissingOutputPath(zig_exe: []const u8, dir_path: []const u8) !void {
- _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" });
- const output_path = try fs.path.join(a, &[_][]const u8{ "does", "not", "exist" });
+ _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" });
+ const output_path = try fs.path.join(a, &[_][]const u8{ "does", "not", "exist", "foo.exe" });
+ const output_arg = try std.fmt.allocPrint(a, "-femit-bin={s}", .{output_path});
const source_path = try fs.path.join(a, &[_][]const u8{ "src", "main.zig" });
- _ = try exec(dir_path, &[_][]const u8{
- zig_exe, "build-exe", source_path, "--output-dir", output_path,
- });
+ const result = try exec(dir_path, false, &[_][]const u8{ zig_exe, "build-exe", source_path, output_arg });
+ const s = std.fs.path.sep_str;
+ const expected: []const u8 = "error: unable to open output directory 'does" ++ s ++ "not" ++ s ++ "exist': FileNotFound\n";
+ testing.expectEqualStrings(expected, result.stderr);
}
fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void {
- _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" });
+ _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" });
const unformatted_code = " // no reason for indent";
const fmt1_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt1.zig" });
try fs.cwd().writeFile(fmt1_zig_path, unformatted_code);
- const run_result1 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", fmt1_zig_path });
+ const run_result1 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", fmt1_zig_path });
// stderr should be file path + \n
testing.expect(std.mem.startsWith(u8, run_result1.stderr, fmt1_zig_path));
testing.expect(run_result1.stderr.len == fmt1_zig_path.len + 1 and run_result1.stderr[run_result1.stderr.len - 1] == '\n');
@@ -161,12 +166,12 @@ fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void {
const fmt2_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt2.zig" });
try fs.cwd().writeFile(fmt2_zig_path, unformatted_code);
- const run_result2 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", dir_path });
+ const run_result2 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path });
// running it on the dir, only the new file should be changed
testing.expect(std.mem.startsWith(u8, run_result2.stderr, fmt2_zig_path));
testing.expect(run_result2.stderr.len == fmt2_zig_path.len + 1 and run_result2.stderr[run_result2.stderr.len - 1] == '\n');
- const run_result3 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", dir_path });
+ const run_result3 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path });
// both files have been formatted, nothing should change now
testing.expect(run_result3.stderr.len == 0);
}
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index f457c74609..7a33de4f19 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -2,6 +2,14 @@ const tests = @import("tests.zig");
const std = @import("std");
pub fn addCases(cases: *tests.CompileErrorContext) void {
+ cases.add("slice sentinel mismatch",
+ \\export fn entry() void {
+ \\ const x = @import("std").meta.Vector(3, f32){ 25, 75, 5, 0 };
+ \\}
+ , &[_][]const u8{
+ "tmp.zig:2:62: error: index 3 outside vector of size 3",
+ });
+
cases.add("slice sentinel mismatch",
\\export fn entry() void {
\\ const y: [:1]const u8 = &[_:2]u8{ 1, 2 };
@@ -2347,7 +2355,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ exit(0);
\\}
, &[_][]const u8{
- "tmp.zig:3:5: error: dependency on library c must be explicitly specified in the build command",
+ "tmp.zig:3:5: error: dependency on libc must be explicitly specified in the build command",
});
cases.addTest("libc headers note",
@@ -7548,7 +7556,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
});
cases.add( // fixed bug #2032
- "compile diagnostic string for top level decl type",
+ "compile diagnostic string for top level decl type",
\\export fn entry() void {
\\ var foo: u32 = @This(){};
\\}
diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig
index 8bdffcd500..dc9e49da43 100644
--- a/test/stage1/behavior/vector.zig
+++ b/test/stage1/behavior/vector.zig
@@ -274,6 +274,14 @@ test "vector comparison operators" {
expectEqual(@splat(4, true), v1 != v3);
expectEqual(@splat(4, false), v1 != v2);
}
+ {
+ // Comptime-known LHS/RHS
+ var v1: @Vector(4, u32) = [_]u32{ 2, 1, 2, 1 };
+ const v2 = @splat(4, @as(u32, 2));
+ const v3: @Vector(4, bool) = [_]bool{ true, false, true, false };
+ expectEqual(v3, v1 == v2);
+ expectEqual(v3, v2 == v1);
+ }
}
};
S.doTheTest();
diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig
index 0608221866..2a176e0368 100644
--- a/test/stage2/cbe.zig
+++ b/test/stage2/cbe.zig
@@ -1,5 +1,5 @@
const std = @import("std");
-const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
+const TestContext = @import("../../src/test.zig").TestContext;
// These tests should work with all platforms, but we're using linux_x64 for
// now for consistency. Will be expanded eventually.
diff --git a/test/stage2/spu-ii.zig b/test/stage2/spu-ii.zig
index 1316f19d0d..aa091c4174 100644
--- a/test/stage2/spu-ii.zig
+++ b/test/stage2/spu-ii.zig
@@ -1,5 +1,5 @@
const std = @import("std");
-const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
+const TestContext = @import("../../src/test.zig").TestContext;
const spu = std.zig.CrossTarget{
.cpu_arch = .spu_2,
diff --git a/test/stage2/test.zig b/test/stage2/test.zig
index ad81e463b9..a22dc23d36 100644
--- a/test/stage2/test.zig
+++ b/test/stage2/test.zig
@@ -1,8 +1,11 @@
const std = @import("std");
-const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
+const TestContext = @import("../../src/test.zig").TestContext;
+
+// Self-hosted has differing levels of support for various architectures. For now we pass explicit
+// target parameters to each test case. At some point we will take this to the next level and have
+// a set of targets that all test cases run on unless specifically overridden. For now, each test
+// case applies to only the specified target.
-// self-hosted does not yet support PE executable files / COFF object files
-// or mach-o files. So we do these test cases cross compiling for x86_64-linux.
const linux_x64 = std.zig.CrossTarget{
.cpu_arch = .x86_64,
.os_tag = .linux,
@@ -73,7 +76,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"Hello, World!\n",
);
// Now change the message only
@@ -105,7 +108,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"What is up? This is a longer message that will force the data to be relocated in virtual address space.\n",
);
// Now we print it twice.
@@ -181,7 +184,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"Hello, World!\n",
);
}
@@ -216,7 +219,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"Hello, World!\n",
);
}
@@ -241,7 +244,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"Hello, World!\n",
);
}
@@ -268,7 +271,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
}
@@ -295,7 +298,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
}
@@ -326,7 +329,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -359,7 +362,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -395,7 +398,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -432,7 +435,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -462,7 +465,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -496,7 +499,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -520,7 +523,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -559,7 +562,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"hello\nhello\nhello\nhello\n",
);
@@ -596,7 +599,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -638,7 +641,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -690,7 +693,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -752,7 +755,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -785,7 +788,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -817,7 +820,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -842,7 +845,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -868,7 +871,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -901,7 +904,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"hello\nhello\nhello\nhello\nhello\n",
);
}
@@ -920,7 +923,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ bar();
\\}
\\fn bar() void {}
- ,
+ ,
"42\n",
);
@@ -938,7 +941,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ bar();
\\}
\\fn bar() void {}
- ,
+ ,
"42\n",
);
@@ -954,7 +957,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ bar();
\\}
\\fn bar() void {}
- ,
+ ,
// This is what you get when you take the bits of the IEE-754
// representation of 42.0 and reinterpret them as an unsigned
// integer. Guess that's a bug in wasmtime.
diff --git a/test/stage2/zir.zig b/test/stage2/zir.zig
index 78d971d104..f87fca3748 100644
--- a/test/stage2/zir.zig
+++ b/test/stage2/zir.zig
@@ -1,5 +1,5 @@
const std = @import("std");
-const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
+const TestContext = @import("../../src/test.zig").TestContext;
// self-hosted does not yet support PE executable files / COFF object files
// or mach-o files. So we do the ZIR transform test cases cross compiling for
// x86_64-linux.
@@ -156,7 +156,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ %0 = call(@a, [])
\\ %1 = returnvoid()
\\})
- ,
+ ,
&[_][]const u8{
":18:21: error: message",
},
diff --git a/test/tests.zig b/test/tests.zig
index 6598a05ea7..58b2b50094 100644
--- a/test/tests.zig
+++ b/test/tests.zig
@@ -634,7 +634,7 @@ pub const StackTracesContext = struct {
warn("Test {}/{} {}...", .{ self.test_index + 1, self.context.test_index, self.name });
- const child = std.ChildProcess.init(args.span(), b.allocator) catch unreachable;
+ const child = std.ChildProcess.init(args.items, b.allocator) catch unreachable;
defer child.deinit();
child.stdin_behavior = .Ignore;
@@ -643,7 +643,7 @@ pub const StackTracesContext = struct {
child.env_map = b.env_map;
if (b.verbose) {
- printInvocation(args.span());
+ printInvocation(args.items);
}
child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", .{ full_exe_path, @errorName(err) });
@@ -666,23 +666,23 @@ pub const StackTracesContext = struct {
code,
expect_code,
});
- printInvocation(args.span());
+ printInvocation(args.items);
return error.TestFailed;
}
},
.Signal => |signum| {
warn("Process {} terminated on signal {}\n", .{ full_exe_path, signum });
- printInvocation(args.span());
+ printInvocation(args.items);
return error.TestFailed;
},
.Stopped => |signum| {
warn("Process {} stopped on signal {}\n", .{ full_exe_path, signum });
- printInvocation(args.span());
+ printInvocation(args.items);
return error.TestFailed;
},
.Unknown => |code| {
warn("Process {} terminated unexpectedly with error code {}\n", .{ full_exe_path, code });
- printInvocation(args.span());
+ printInvocation(args.items);
return error.TestFailed;
},
}
@@ -837,34 +837,27 @@ pub const CompileErrorContext = struct {
} else {
try zig_args.append("build-obj");
}
- const root_src_basename = self.case.sources.span()[0].filename;
+ const root_src_basename = self.case.sources.items[0].filename;
try zig_args.append(self.write_src.getOutputPath(root_src_basename));
zig_args.append("--name") catch unreachable;
zig_args.append("test") catch unreachable;
- zig_args.append("--output-dir") catch unreachable;
- zig_args.append(b.pathFromRoot(b.cache_root)) catch unreachable;
-
if (!self.case.target.isNative()) {
try zig_args.append("-target");
try zig_args.append(try self.case.target.zigTriple(b.allocator));
}
- switch (self.build_mode) {
- Mode.Debug => {},
- Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable,
- Mode.ReleaseFast => zig_args.append("--release-fast") catch unreachable,
- Mode.ReleaseSmall => zig_args.append("--release-small") catch unreachable,
- }
+ zig_args.append("-O") catch unreachable;
+ zig_args.append(@tagName(self.build_mode)) catch unreachable;
warn("Test {}/{} {}...", .{ self.test_index + 1, self.context.test_index, self.name });
if (b.verbose) {
- printInvocation(zig_args.span());
+ printInvocation(zig_args.items);
}
- const child = std.ChildProcess.init(zig_args.span(), b.allocator) catch unreachable;
+ const child = std.ChildProcess.init(zig_args.items, b.allocator) catch unreachable;
defer child.deinit();
child.env_map = b.env_map;
@@ -886,19 +879,19 @@ pub const CompileErrorContext = struct {
switch (term) {
.Exited => |code| {
if (code == 0) {
- printInvocation(zig_args.span());
+ printInvocation(zig_args.items);
return error.CompilationIncorrectlySucceeded;
}
},
else => {
warn("Process {} terminated unexpectedly\n", .{b.zig_exe});
- printInvocation(zig_args.span());
+ printInvocation(zig_args.items);
return error.TestFailed;
},
}
- const stdout = stdout_buf.span();
- const stderr = stderr_buf.span();
+ const stdout = stdout_buf.items;
+ const stderr = stderr_buf.items;
if (stdout.len != 0) {
warn(
@@ -927,12 +920,12 @@ pub const CompileErrorContext = struct {
if (!ok) {
warn("\n======== Expected these compile errors: ========\n", .{});
- for (self.case.expected_errors.span()) |expected| {
+ for (self.case.expected_errors.items) |expected| {
warn("{}\n", .{expected});
}
}
} else {
- for (self.case.expected_errors.span()) |expected| {
+ for (self.case.expected_errors.items) |expected| {
if (mem.indexOf(u8, stderr, expected) == null) {
warn(
\\
@@ -1032,7 +1025,7 @@ pub const CompileErrorContext = struct {
if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
}
const write_src = b.addWriteFiles();
- for (case.sources.span()) |src_file| {
+ for (case.sources.items) |src_file| {
write_src.add(src_file.filename, src_file.source);
}
@@ -1079,7 +1072,7 @@ pub const StandaloneContext = struct {
zig_args.append("--verbose") catch unreachable;
}
- const run_cmd = b.addSystemCommand(zig_args.span());
+ const run_cmd = b.addSystemCommand(zig_args.items);
const log_step = b.addLog("PASS {}\n", .{annotated_case_name});
log_step.step.dependOn(&run_cmd.step);
@@ -1179,7 +1172,7 @@ pub const GenHContext = struct {
const full_h_path = self.obj.getOutputHPath();
const actual_h = try io.readFileAlloc(b.allocator, full_h_path);
- for (self.case.expected_lines.span()) |expected_line| {
+ for (self.case.expected_lines.items) |expected_line| {
if (mem.indexOf(u8, actual_h, expected_line) == null) {
warn(
\\
@@ -1240,7 +1233,7 @@ pub const GenHContext = struct {
}
const write_src = b.addWriteFiles();
- for (case.sources.span()) |src_file| {
+ for (case.sources.items) |src_file| {
write_src.add(src_file.filename, src_file.source);
}
diff --git a/tools/update_clang_options.zig b/tools/update_clang_options.zig
index 31ef58266d..8b7811aa26 100644
--- a/tools/update_clang_options.zig
+++ b/tools/update_clang_options.zig
@@ -116,19 +116,19 @@ const known_options = [_]KnownOpt{
},
.{
.name = "E",
- .ident = "pp_or_asm",
+ .ident = "preprocess_only",
},
.{
.name = "preprocess",
- .ident = "pp_or_asm",
+ .ident = "preprocess_only",
},
.{
.name = "S",
- .ident = "pp_or_asm",
+ .ident = "asm_only",
},
.{
.name = "assemble",
- .ident = "pp_or_asm",
+ .ident = "asm_only",
},
.{
.name = "O1",