diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b81669a30dcf5eac7e745e8f0e977020aebf6d5..5e342e42e2ec8efa03e7bdb993eb89e2f26c3884 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,7 +51,6 @@ 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") @@ -72,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") @@ -101,7 +95,7 @@ if(APPLE AND ZIG_WORKAROUND_4799) 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. @@ -267,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" ) @@ -334,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 ( @@ -346,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 @@ -411,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}) @@ -430,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") @@ -499,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 2f0d65f5841fdafe93d3f9deeb2935e39b439132..ed31cbb1de6a7ab8a6af98d790c373e17d3ed8a6 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 @@ -82,6 +84,41 @@ in which case you can try `-DZIG_WORKAROUND_6087=ON`. 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 a6a2d873716db0c031cfbb8094f96cd745434a04..b4d8b71f796c01bdc5211f779af36dc379919cff 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 fb4caf18c0f33dd685371bc33e16aeeecb057612..99647ee063f51551bc1c655d07742080e98ebc38 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 78837664276a37520245bce0ef3e14ea91d9e29c..900baebee332a5bf2e4a835f55e07cc56b77b0d7 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 31aea6c3dc084e14f134b1b206b0bd97984a09e4..3d9eb7373516fecb050f1395b19af19e69ed6c19 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 af4d2530d05bd8b72588bf31bd026eb79437fc34..50523d0948863c3dc58ddbac1450d91acbfd06a8 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 6b8b07e0b38f6f10f45e2d7532bcd7288f9953e2..22faf7fd8ff23b40142457b4cc071b981f842508 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);
@@ -9881,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#}
@@ -11385,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 f8c3623ef2f5b56b58ca758f4937605c0b2bb408..649c1e1055b1df7ac8608a19b3687d2ce216d33f 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/build.zig b/lib/std/build.zig
index c0d7f0b8ed10cc1dcb35359cd40f7f5397bc094b..7e3c75bc78a8d725ba618f17279388f99b1583f0 100644
--- a/lib/std/build.zig
+++ b/lib/std/build.zig
@@ -1384,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() });
@@ -1699,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;
}
@@ -1961,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));
},
@@ -1972,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();
@@ -1996,13 +1993,26 @@ 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));
},
@@ -2078,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");
@@ -2092,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) {
@@ -2316,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 8ca2b872093f08bd55fdf02cf5d178beb37c8a32..87e153066dcdfc7bea457410bb60f03be40f0507 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 5cd8194e218306575dd15d3b7a983cd091619444..0000000000000000000000000000000000000000
--- 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 9219b0508872a36e52e361a326990b66ecfde70c..e706302ebd205f7da6df338af7f99acf13406af6 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/log.zig b/lib/std/log.zig
index 7b677f698acb6a019fde3848378dd2a85c9f6c68..0cc2b544522cbb108481ec952f5c8d60157d3070 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 326a73b915af8a6af41fc3160561be61ce148898..4511acb27502ab1b31ffd094829df2ac9ab1cd27 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/os/windows.zig b/lib/std/os/windows.zig
index a308ee76fc531a540ae5a4f16e99e4ba342b1a7a..fab202bf79b7b93f583ac814e63e47ee7c6da91a 100644
--- a/lib/std/os/windows.zig
+++ b/lib/std/os/windows.zig
@@ -761,6 +761,7 @@ pub const DeleteFileError = error{
FileNotFound,
AccessDenied,
NameTooLong,
+ /// Also known as sharing violation.
FileBusy,
Unexpected,
NotDir,
@@ -825,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/special/build_runner.zig b/lib/std/special/build_runner.zig
index 3ab74a11a22093e12dc3dfd522a0adf90384150c..43d6b965364757ee43637b1dfa60cfaaaf9cde54 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 b9452b79cc36257721b30ec12eea20d2a9ec4501..14a35a3aaa74094bff6a131d82f97aa222824bab 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 aea31a1531a831b11fd147e198bddb91e4f20739..71940b12cafc3338255fe682ad0649bb62087a2d 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 e21e428c7785062261f5938a1fc6c5c80a507f3d..62f7f21f4e3f371e346c5d9ee6ebbc66f0573a31 100644
--- a/lib/std/std.zig
+++ b/lib/std/std.zig
@@ -47,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");
diff --git a/lib/std/target.zig b/lib/std/target.zig
index 37425a9a29435e641fc4ab25789ad82513c56021..99617c6d0eba57bcacae1c951cbb526c0767fe17 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 658d31bb8272c82574b830b6cf84dfd31818227f..ccbd1d6324fa8d0fd43b1db1e9aa682db6b595fd 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 1dedce406764b9a96b18fe746bae5dd649d7614e..cc1815e8e50f827c7417117097606de92111aee5 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 });
- },
- .Obj => return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, target.oFileExt() }),
+ 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() }),
+ },
+ .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/cross_target.zig b/lib/std/zig/cross_target.zig
index 75fc5969a5c282d9ba1b95c88ce488bb71927ecb..f1ae3457a564b9886b9b15cffa3925b893168fd3 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/system.zig b/lib/std/zig/system.zig
index 37eea36d47f14f36fcdc68e42466780bcac2d1a1..d13d0b22effb6ee0822f34482419a83e01ff1f16 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/src-self-hosted/Module.zig b/src-self-hosted/Module.zig
deleted file mode 100644
index dc48ae23e7f821c5c249124469d33a1ac997ebf1..0000000000000000000000000000000000000000
--- a/src-self-hosted/Module.zig
+++ /dev/null
@@ -1,3581 +0,0 @@
-const std = @import("std");
-const mem = std.mem;
-const Allocator = std.mem.Allocator;
-const ArrayListUnmanaged = std.ArrayListUnmanaged;
-const Value = @import("value.zig").Value;
-const Type = @import("type.zig").Type;
-const TypedValue = @import("TypedValue.zig");
-const assert = std.debug.assert;
-const log = std.log.scoped(.module);
-const BigIntConst = std.math.big.int.Const;
-const BigIntMutable = std.math.big.int.Mutable;
-const Target = std.Target;
-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.
-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.
-decl_exports: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},
-/// We track which export is associated with the given symbol name for quick
-/// detection of symbol collisions.
-symbol_exports: std.StringArrayHashMapUnmanaged(*Export) = .{},
-/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
-/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
-/// is performing the export of another Decl.
-/// This table owns the Export memory.
-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.
-/// 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) = .{},
-/// 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) = .{},
-/// 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) = .{},
-
-/// 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,
-};
-
-pub const Export = struct {
- options: std.builtin.ExportOptions,
- /// Byte offset into the file that contains the export directive.
- src: usize,
- /// Represents the position of the export, if any, in the output file.
- link: link.File.Elf.Export,
- /// The Decl that performs the export. Note that this is *not* the Decl being exported.
- owner_decl: *Decl,
- /// The Decl being exported. Note this is *not* the Decl performing the export.
- exported_decl: *Decl,
- status: enum {
- in_progress,
- failed,
- /// Indicates that the failure was due to a temporary issue, such as an I/O error
- /// when writing to the output file. Retrying the export may succeed.
- failed_retryable,
- complete,
- },
-};
-
-pub const Decl = struct {
- /// This name is relative to the containing namespace of the decl. It uses a null-termination
- /// to save bytes, since there can be a lot of decls in a compilation. The null byte is not allowed
- /// in symbol names, because executable file formats use null-terminated strings for symbol names.
- /// All Decls have names, even values that are not bound to a zig namespace. This is necessary for
- /// mapping them to an address in the output file.
- /// Memory owned by this decl, using Module's allocator.
- name: [*:0]const u8,
- /// The direct parent container of the Decl. This is either a `Scope.Container` or `Scope.ZIRModule`.
- /// Reference to externally owned memory.
- scope: *Scope,
- /// The AST Node decl index or ZIR Inst index that contains this declaration.
- /// Must be recomputed when the corresponding source file is modified.
- src_index: usize,
- /// The most recent value of the Decl after a successful semantic analysis.
- typed_value: union(enum) {
- never_succeeded: void,
- most_recent: TypedValue.Managed,
- },
- /// Represents the "shallow" analysis status. For example, for decls that are functions,
- /// the function type is analyzed with this set to `in_progress`, however, the semantic
- /// analysis of the function body is performed with this value set to `success`. Functions
- /// have their own analysis status field.
- analysis: enum {
- /// This Decl corresponds to an AST Node that has not been referenced yet, and therefore
- /// because of Zig's lazy declaration analysis, it will remain unanalyzed until referenced.
- unreferenced,
- /// Semantic analysis for this Decl is running right now. This state detects dependency loops.
- in_progress,
- /// This Decl might be OK but it depends on another one which did not successfully complete
- /// semantic analysis.
- dependency_failure,
- /// Semantic analysis failure.
- /// There will be a corresponding ErrorMsg in Module.failed_decls.
- sema_failure,
- /// There will be a corresponding ErrorMsg in Module.failed_decls.
- /// This indicates the failure was something like running out of disk space,
- /// and attempting semantic analysis again may succeed.
- sema_failure_retryable,
- /// There will be a corresponding ErrorMsg in Module.failed_decls.
- codegen_failure,
- /// There will be a corresponding ErrorMsg in Module.failed_decls.
- /// This indicates the failure was something like running out of disk space,
- /// and attempting codegen again may succeed.
- codegen_failure_retryable,
- /// Everything is done. During an update, this Decl may be out of date, depending
- /// on its dependencies. The `generation` field can be used to determine if this
- /// completion status occurred before or after a given update.
- complete,
- /// A Module update is in progress, and this Decl has been flagged as being known
- /// to require re-analysis.
- outdated,
- },
- /// This flag is set when this Decl is added to a check_for_deletion set, and cleared
- /// when removed.
- deletion_flag: bool,
- /// Whether the corresponding AST decl has a `pub` keyword.
- is_pub: bool,
-
- /// An integer that can be checked against the corresponding incrementing
- /// generation field of Module. This is used to determine whether `complete` status
- /// represents pre- or post- re-analysis.
- generation: u32,
-
- /// Represents the position of the code in the output file.
- /// This is populated regardless of semantic analysis and code generation.
- link: link.File.LinkBlock,
-
- /// Represents the function in the linked output file, if the `Decl` is a function.
- /// This is stored here and not in `Fn` because `Decl` survives across updates but
- /// `Fn` does not.
- /// TODO Look into making `Fn` a longer lived structure and moving this field there
- /// to save on memory usage.
- fn_link: link.File.LinkFn,
-
- contents_hash: std.zig.SrcHash,
-
- /// The shallow set of other decls whose typed_value could possibly change if this Decl's
- /// typed_value is modified.
- dependants: DepsTable = .{},
- /// The shallow set of other decls whose typed_value changing indicates that this Decl's
- /// typed_value may need to be regenerated.
- dependencies: DepsTable = .{},
-
- /// The reason this is not `std.AutoArrayHashMapUnmanaged` is a workaround for
- /// stage1 compiler giving me: `error: struct 'Module.Decl' depends on itself`
- pub const DepsTable = std.ArrayHashMapUnmanaged(*Decl, void, std.array_hash_map.getAutoHashFn(*Decl), std.array_hash_map.getAutoEqlFn(*Decl), false);
-
- pub fn destroy(self: *Decl, gpa: *Allocator) void {
- gpa.free(mem.spanZ(self.name));
- if (self.typedValueManaged()) |tvm| {
- tvm.deinit(gpa);
- }
- self.dependants.deinit(gpa);
- self.dependencies.deinit(gpa);
- gpa.destroy(self);
- }
-
- pub fn src(self: Decl) usize {
- switch (self.scope.tag) {
- .container => {
- const container = @fieldParentPtr(Scope.Container, "base", self.scope);
- const tree = container.file_scope.contents.tree;
- // TODO Container should have it's own decls()
- const decl_node = tree.root_node.decls()[self.src_index];
- return tree.token_locs[decl_node.firstToken()].start;
- },
- .zir_module => {
- const zir_module = @fieldParentPtr(Scope.ZIRModule, "base", self.scope);
- const module = zir_module.contents.module;
- const src_decl = module.decls[self.src_index];
- return src_decl.inst.src;
- },
- .file, .block => unreachable,
- .gen_zir => unreachable,
- .local_val => unreachable,
- .local_ptr => unreachable,
- .decl => unreachable,
- }
- }
-
- pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash {
- return self.scope.fullyQualifiedNameHash(mem.spanZ(self.name));
- }
-
- pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue {
- const tvm = self.typedValueManaged() orelse return error.AnalysisFail;
- return tvm.typed_value;
- }
-
- pub fn value(self: *Decl) error{AnalysisFail}!Value {
- return (try self.typedValue()).val;
- }
-
- pub fn dump(self: *Decl) void {
- const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src);
- std.debug.print("{}:{}:{} name={} status={}", .{
- self.scope.sub_file_path,
- loc.line + 1,
- loc.column + 1,
- mem.spanZ(self.name),
- @tagName(self.analysis),
- });
- if (self.typedValueManaged()) |tvm| {
- std.debug.print(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val });
- }
- std.debug.print("\n", .{});
- }
-
- pub fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {
- switch (self.typed_value) {
- .most_recent => |*x| return x,
- .never_succeeded => return null,
- }
- }
-
- fn removeDependant(self: *Decl, other: *Decl) void {
- self.dependants.removeAssertDiscard(other);
- }
-
- fn removeDependency(self: *Decl, other: *Decl) void {
- self.dependencies.removeAssertDiscard(other);
- }
-};
-
-/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
-pub const Fn = struct {
- /// This memory owned by the Decl's TypedValue.Managed arena allocator.
- analysis: union(enum) {
- queued: *ZIR,
- in_progress,
- /// There will be a corresponding ErrorMsg in Module.failed_decls
- sema_failure,
- /// This Fn might be OK but it depends on another Decl which did not successfully complete
- /// semantic analysis.
- dependency_failure,
- success: Body,
- },
- owner_decl: *Decl,
-
- /// This memory is temporary and points to stack memory for the duration
- /// of Fn analysis.
- pub const Analysis = struct {
- inner_block: Scope.Block,
- };
-
- /// Contains un-analyzed ZIR instructions generated from Zig source AST.
- pub const ZIR = struct {
- body: zir.Module.Body,
- arena: std.heap.ArenaAllocator.State,
- };
-
- /// For debugging purposes.
- pub fn dump(self: *Fn, mod: Module) void {
- std.debug.print("Module.Function(name={}) ", .{self.owner_decl.name});
- switch (self.analysis) {
- .queued => {
- std.debug.print("queued\n", .{});
- },
- .in_progress => {
- std.debug.print("in_progress\n", .{});
- },
- else => {
- std.debug.print("\n", .{});
- zir.dumpFn(mod, self);
- },
- }
- }
-};
-
-pub const Var = struct {
- /// if is_extern == true this is undefined
- init: Value,
- owner_decl: *Decl,
-
- is_extern: bool,
- is_mutable: bool,
- is_threadlocal: bool,
-};
-
-pub const Scope = struct {
- tag: Tag,
-
- pub const NameHash = [16]u8;
-
- pub fn cast(base: *Scope, comptime T: type) ?*T {
- if (base.tag != T.base_tag)
- return null;
-
- return @fieldParentPtr(T, "base", base);
- }
-
- /// Asserts the scope has a parent which is a DeclAnalysis and
- /// returns the arena Allocator.
- pub fn arena(self: *Scope) *Allocator {
- switch (self.tag) {
- .block => return self.cast(Block).?.arena,
- .decl => return &self.cast(DeclAnalysis).?.arena.allocator,
- .gen_zir => return self.cast(GenZIR).?.arena,
- .local_val => return self.cast(LocalVal).?.gen_zir.arena,
- .local_ptr => return self.cast(LocalPtr).?.gen_zir.arena,
- .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,
- .file => unreachable,
- .container => unreachable,
- }
- }
-
- /// If the scope has a parent which is a `DeclAnalysis`,
- /// returns the `Decl`, otherwise returns `null`.
- pub fn decl(self: *Scope) ?*Decl {
- return switch (self.tag) {
- .block => self.cast(Block).?.decl,
- .gen_zir => self.cast(GenZIR).?.decl,
- .local_val => self.cast(LocalVal).?.gen_zir.decl,
- .local_ptr => self.cast(LocalPtr).?.gen_zir.decl,
- .decl => self.cast(DeclAnalysis).?.decl,
- .zir_module => null,
- .file => null,
- .container => null,
- };
- }
-
- /// Asserts the scope has a parent which is a ZIRModule or Container and
- /// returns it.
- pub fn namespace(self: *Scope) *Scope {
- switch (self.tag) {
- .block => return self.cast(Block).?.decl.scope,
- .gen_zir => return self.cast(GenZIR).?.decl.scope,
- .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope,
- .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope,
- .decl => return self.cast(DeclAnalysis).?.decl.scope,
- .file => return &self.cast(File).?.root_container.base,
- .zir_module, .container => return self,
- }
- }
-
- /// Must generate unique bytes with no collisions with other decls.
- /// The point of hashing here is only to limit the number of bytes of
- /// the unique identifier to a fixed size (16 bytes).
- pub fn fullyQualifiedNameHash(self: *Scope, name: []const u8) NameHash {
- switch (self.tag) {
- .block => unreachable,
- .gen_zir => unreachable,
- .local_val => unreachable,
- .local_ptr => unreachable,
- .decl => unreachable,
- .file => unreachable,
- .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name),
- .container => return self.cast(Container).?.fullyQualifiedNameHash(name),
- }
- }
-
- /// Asserts the scope is a child of a File and has an AST tree and returns the tree.
- pub fn tree(self: *Scope) *ast.Tree {
- switch (self.tag) {
- .file => return self.cast(File).?.contents.tree,
- .zir_module => unreachable,
- .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(Container).?.file_scope.contents.tree,
- .block => return self.cast(Block).?.decl.scope.cast(Container).?.file_scope.contents.tree,
- .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(Container).?.file_scope.contents.tree,
- .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree,
- .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree,
- .container => return self.cast(Container).?.file_scope.contents.tree,
- }
- }
-
- /// Asserts the scope is a child of a `GenZIR` and returns it.
- pub fn getGenZIR(self: *Scope) *GenZIR {
- return switch (self.tag) {
- .block => unreachable,
- .gen_zir => self.cast(GenZIR).?,
- .local_val => return self.cast(LocalVal).?.gen_zir,
- .local_ptr => return self.cast(LocalPtr).?.gen_zir,
- .decl => unreachable,
- .zir_module => unreachable,
- .file => unreachable,
- .container => unreachable,
- };
- }
-
- /// Asserts the scope has a parent which is a ZIRModule, Contaienr or File and
- /// returns the sub_file_path field.
- pub fn subFilePath(base: *Scope) []const u8 {
- switch (base.tag) {
- .container => return @fieldParentPtr(Container, "base", base).file_scope.sub_file_path,
- .file => return @fieldParentPtr(File, "base", base).sub_file_path,
- .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path,
- .block => unreachable,
- .gen_zir => unreachable,
- .local_val => unreachable,
- .local_ptr => unreachable,
- .decl => unreachable,
- }
- }
-
- pub fn unload(base: *Scope, gpa: *Allocator) void {
- switch (base.tag) {
- .file => return @fieldParentPtr(File, "base", base).unload(gpa),
- .zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(gpa),
- .block => unreachable,
- .gen_zir => unreachable,
- .local_val => unreachable,
- .local_ptr => unreachable,
- .decl => unreachable,
- .container => unreachable,
- }
- }
-
- pub fn getSource(base: *Scope, module: *Module) ![:0]const u8 {
- switch (base.tag) {
- .container => return @fieldParentPtr(Container, "base", base).file_scope.getSource(module),
- .file => return @fieldParentPtr(File, "base", base).getSource(module),
- .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module),
- .gen_zir => unreachable,
- .local_val => unreachable,
- .local_ptr => unreachable,
- .block => unreachable,
- .decl => unreachable,
- }
- }
-
- /// Asserts the scope is a namespace Scope and removes the Decl from the namespace.
- pub fn removeDecl(base: *Scope, child: *Decl) void {
- switch (base.tag) {
- .container => return @fieldParentPtr(Container, "base", base).removeDecl(child),
- .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child),
- .file => unreachable,
- .block => unreachable,
- .gen_zir => unreachable,
- .local_val => unreachable,
- .local_ptr => unreachable,
- .decl => unreachable,
- }
- }
-
- /// Asserts the scope is a File or ZIRModule and deinitializes it, then deallocates it.
- pub fn destroy(base: *Scope, gpa: *Allocator) void {
- switch (base.tag) {
- .file => {
- const scope_file = @fieldParentPtr(File, "base", base);
- scope_file.deinit(gpa);
- gpa.destroy(scope_file);
- },
- .zir_module => {
- const scope_zir_module = @fieldParentPtr(ZIRModule, "base", base);
- scope_zir_module.deinit(gpa);
- gpa.destroy(scope_zir_module);
- },
- .block => unreachable,
- .gen_zir => unreachable,
- .local_val => unreachable,
- .local_ptr => unreachable,
- .decl => unreachable,
- .container => unreachable,
- }
- }
-
- fn name_hash_hash(x: NameHash) u32 {
- return @truncate(u32, @bitCast(u128, x));
- }
-
- fn name_hash_eql(a: NameHash, b: NameHash) bool {
- return @bitCast(u128, a) == @bitCast(u128, b);
- }
-
- pub const Tag = enum {
- /// .zir source code.
- zir_module,
- /// .zig source code.
- file,
- /// struct, enum or union, every .file contains one of these.
- container,
- block,
- decl,
- gen_zir,
- local_val,
- local_ptr,
- };
-
- pub const Container = struct {
- pub const base_tag: Tag = .container;
- base: Scope = Scope{ .tag = base_tag },
-
- file_scope: *Scope.File,
-
- /// Direct children of the file.
- decls: std.AutoArrayHashMapUnmanaged(*Decl, void),
-
- // TODO implement container types and put this in a status union
- // ty: Type
-
- pub fn deinit(self: *Container, gpa: *Allocator) void {
- self.decls.deinit(gpa);
- self.* = undefined;
- }
-
- pub fn removeDecl(self: *Container, child: *Decl) void {
- _ = self.decls.remove(child);
- }
-
- pub fn fullyQualifiedNameHash(self: *Container, name: []const u8) NameHash {
- // TODO container scope qualified names.
- return std.zig.hashSrc(name);
- }
- };
-
- pub const File = struct {
- pub const base_tag: Tag = .file;
- base: Scope = Scope{ .tag = base_tag },
-
- /// Relative to the owning package's root_src_dir.
- /// Reference to external memory, not owned by File.
- sub_file_path: []const u8,
- source: union(enum) {
- unloaded: void,
- bytes: [:0]const u8,
- },
- contents: union {
- not_available: void,
- tree: *ast.Tree,
- },
- status: enum {
- never_loaded,
- unloaded_success,
- unloaded_parse_failure,
- loaded_success,
- },
-
- root_container: Container,
-
- pub fn unload(self: *File, gpa: *Allocator) void {
- switch (self.status) {
- .never_loaded,
- .unloaded_parse_failure,
- .unloaded_success,
- => {},
-
- .loaded_success => {
- self.contents.tree.deinit();
- self.status = .unloaded_success;
- },
- }
- switch (self.source) {
- .bytes => |bytes| {
- gpa.free(bytes);
- self.source = .{ .unloaded = {} };
- },
- .unloaded => {},
- }
- }
-
- pub fn deinit(self: *File, gpa: *Allocator) void {
- self.root_container.deinit(gpa);
- self.unload(gpa);
- self.* = undefined;
- }
-
- pub fn dumpSrc(self: *File, src: usize) void {
- const loc = std.zig.findLineColumn(self.source.bytes, src);
- std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
- }
-
- pub fn getSource(self: *File, module: *Module) ![:0]const u8 {
- switch (self.source) {
- .unloaded => {
- const source = try module.root_pkg.root_src_dir.readFileAllocOptions(
- module.gpa,
- self.sub_file_path,
- std.math.maxInt(u32),
- null,
- 1,
- 0,
- );
- self.source = .{ .bytes = source };
- return source;
- },
- .bytes => |bytes| return bytes,
- }
- }
- };
-
- pub const ZIRModule = struct {
- pub const base_tag: Tag = .zir_module;
- base: Scope = Scope{ .tag = base_tag },
- /// Relative to the owning package's root_src_dir.
- /// Reference to external memory, not owned by ZIRModule.
- sub_file_path: []const u8,
- source: union(enum) {
- unloaded: void,
- bytes: [:0]const u8,
- },
- contents: union {
- not_available: void,
- module: *zir.Module,
- },
- status: enum {
- never_loaded,
- unloaded_success,
- unloaded_parse_failure,
- unloaded_sema_failure,
-
- loaded_sema_failure,
- loaded_success,
- },
-
- /// Even though .zir files only have 1 module, this set is still needed
- /// because of anonymous Decls, which can exist in the global set, but
- /// not this one.
- decls: ArrayListUnmanaged(*Decl),
-
- pub fn unload(self: *ZIRModule, gpa: *Allocator) void {
- switch (self.status) {
- .never_loaded,
- .unloaded_parse_failure,
- .unloaded_sema_failure,
- .unloaded_success,
- => {},
-
- .loaded_success => {
- self.contents.module.deinit(gpa);
- gpa.destroy(self.contents.module);
- self.contents = .{ .not_available = {} };
- self.status = .unloaded_success;
- },
- .loaded_sema_failure => {
- self.contents.module.deinit(gpa);
- gpa.destroy(self.contents.module);
- self.contents = .{ .not_available = {} };
- self.status = .unloaded_sema_failure;
- },
- }
- switch (self.source) {
- .bytes => |bytes| {
- gpa.free(bytes);
- self.source = .{ .unloaded = {} };
- },
- .unloaded => {},
- }
- }
-
- pub fn deinit(self: *ZIRModule, gpa: *Allocator) void {
- self.decls.deinit(gpa);
- self.unload(gpa);
- self.* = undefined;
- }
-
- pub fn removeDecl(self: *ZIRModule, child: *Decl) void {
- for (self.decls.items) |item, i| {
- if (item == child) {
- _ = self.decls.swapRemove(i);
- return;
- }
- }
- }
-
- pub fn dumpSrc(self: *ZIRModule, src: usize) void {
- const loc = std.zig.findLineColumn(self.source.bytes, src);
- std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
- }
-
- pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 {
- switch (self.source) {
- .unloaded => {
- const source = try module.root_pkg.root_src_dir.readFileAllocOptions(
- module.gpa,
- self.sub_file_path,
- std.math.maxInt(u32),
- null,
- 1,
- 0,
- );
- self.source = .{ .bytes = source };
- return source;
- },
- .bytes => |bytes| return bytes,
- }
- }
-
- pub fn fullyQualifiedNameHash(self: *ZIRModule, name: []const u8) NameHash {
- // ZIR modules only have 1 file with all decls global in the same namespace.
- return std.zig.hashSrc(name);
- }
- };
-
- /// This is a temporary structure, references to it are valid only
- /// during semantic analysis of the block.
- pub const Block = struct {
- pub const base_tag: Tag = .block;
- base: Scope = Scope{ .tag = base_tag },
- parent: ?*Block,
- func: ?*Fn,
- decl: *Decl,
- instructions: ArrayListUnmanaged(*Inst),
- /// Points to the arena allocator of DeclAnalysis
- arena: *Allocator,
- label: ?Label = null,
- is_comptime: bool,
-
- pub const Label = struct {
- zir_block: *zir.Inst.Block,
- results: ArrayListUnmanaged(*Inst),
- block_inst: *Inst.Block,
- };
- };
-
- /// This is a temporary structure, references to it are valid only
- /// during semantic analysis of the decl.
- pub const DeclAnalysis = struct {
- pub const base_tag: Tag = .decl;
- base: Scope = Scope{ .tag = base_tag },
- decl: *Decl,
- arena: std.heap.ArenaAllocator,
- };
-
- /// This is a temporary structure, references to it are valid only
- /// during semantic analysis of the decl.
- pub const GenZIR = struct {
- pub const base_tag: Tag = .gen_zir;
- base: Scope = Scope{ .tag = base_tag },
- /// Parents can be: `GenZIR`, `ZIRModule`, `File`
- parent: *Scope,
- decl: *Decl,
- arena: *Allocator,
- /// The first N instructions in a function body ZIR are arg instructions.
- instructions: std.ArrayListUnmanaged(*zir.Inst) = .{},
- label: ?Label = null,
-
- pub const Label = struct {
- token: ast.TokenIndex,
- block_inst: *zir.Inst.Block,
- result_loc: astgen.ResultLoc,
- };
- };
-
- /// This is always a `const` local and importantly the `inst` is a value type, not a pointer.
- /// This structure lives as long as the AST generation of the Block
- /// node that contains the variable.
- pub const LocalVal = struct {
- pub const base_tag: Tag = .local_val;
- base: Scope = Scope{ .tag = base_tag },
- /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`.
- parent: *Scope,
- gen_zir: *GenZIR,
- name: []const u8,
- inst: *zir.Inst,
- };
-
- /// This could be a `const` or `var` local. It has a pointer instead of a value.
- /// This structure lives as long as the AST generation of the Block
- /// node that contains the variable.
- pub const LocalPtr = struct {
- pub const base_tag: Tag = .local_ptr;
- base: Scope = Scope{ .tag = base_tag },
- /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`.
- parent: *Scope,
- gen_zir: *GenZIR,
- name: []const u8,
- ptr: *zir.Inst,
- };
-};
-
-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 fn deinit(self: *Module) void {
- self.bin_file.destroy();
- const gpa = self.gpa;
- self.gpa.free(self.root_name);
- self.deletion_set.deinit(gpa);
- self.work_queue.deinit();
-
- for (self.decl_table.items()) |entry| {
- entry.value.destroy(gpa);
- }
- self.decl_table.deinit(gpa);
-
- for (self.failed_decls.items()) |entry| {
- entry.value.destroy(gpa);
- }
- self.failed_decls.deinit(gpa);
-
- for (self.failed_files.items()) |entry| {
- entry.value.destroy(gpa);
- }
- self.failed_files.deinit(gpa);
-
- for (self.failed_exports.items()) |entry| {
- entry.value.destroy(gpa);
- }
- self.failed_exports.deinit(gpa);
-
- for (self.decl_exports.items()) |entry| {
- const export_list = entry.value;
- gpa.free(export_list);
- }
- self.decl_exports.deinit(gpa);
-
- for (self.export_owners.items()) |entry| {
- freeExportList(gpa, entry.value);
- }
- self.export_owners.deinit(gpa);
-
- self.symbol_exports.deinit(gpa);
- self.root_scope.destroy(gpa);
-
- var it = self.global_error_set.iterator();
- while (it.next()) |entry| {
- gpa.free(entry.key);
- }
- self.global_error_set.deinit(gpa);
- self.* = undefined;
-}
-
-fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
- for (export_list) |exp| {
- gpa.free(exp.options.name);
- gpa.destroy(exp);
- }
- 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();
-
- const subsequent_analysis = switch (decl.analysis) {
- .in_progress => unreachable,
-
- .sema_failure,
- .sema_failure_retryable,
- .codegen_failure,
- .dependency_failure,
- .codegen_failure_retryable,
- => return error.AnalysisFail,
-
- .complete => return,
-
- .outdated => blk: {
- log.debug("re-analyzing {}\n", .{decl.name});
-
- // The exports this Decl performs will be re-discovered, so we remove them here
- // prior to re-analysis.
- self.deleteDeclExports(decl);
- // Dependencies will be re-discovered, so we remove them here prior to re-analysis.
- for (decl.dependencies.items()) |entry| {
- const dep = entry.key;
- dep.removeDependant(decl);
- if (dep.dependants.items().len == 0 and !dep.deletion_flag) {
- // We don't perform a deletion here, because this Decl or another one
- // may end up referencing it before the update is complete.
- dep.deletion_flag = true;
- try self.deletion_set.append(self.gpa, dep);
- }
- }
- decl.dependencies.clearRetainingCapacity();
-
- break :blk true;
- },
-
- .unreferenced => false,
- };
-
- const type_changed = if (self.root_scope.cast(Scope.ZIRModule)) |zir_module|
- try zir_sema.analyzeZirDecl(self, decl, zir_module.contents.module.decls[decl.src_index])
- else
- self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- 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.gpa,
- decl.src(),
- "unable to analyze: {}",
- .{@errorName(err)},
- ));
- decl.analysis = .sema_failure_retryable;
- return error.AnalysisFail;
- },
- };
-
- if (subsequent_analysis) {
- // We may need to chase the dependants and re-analyze them.
- // However, if the decl is a function, and the type is the same, we do not need to.
- if (type_changed or decl.typed_value.most_recent.typed_value.val.tag() != .function) {
- for (decl.dependants.items()) |entry| {
- const dep = entry.key;
- switch (dep.analysis) {
- .unreferenced => unreachable,
- .in_progress => unreachable,
- .outdated => continue, // already queued for update
-
- .dependency_failure,
- .sema_failure,
- .sema_failure_retryable,
- .codegen_failure,
- .codegen_failure_retryable,
- .complete,
- => if (dep.generation != self.generation) {
- try self.markOutdatedDecl(dep);
- },
- }
- }
- }
- }
-}
-
-fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
- const tracy = trace(@src());
- defer tracy.end();
-
- const container_scope = decl.scope.cast(Scope.Container).?;
- const tree = try self.getAstTree(container_scope);
- const ast_node = tree.root_node.decls()[decl.src_index];
- switch (ast_node.tag) {
- .FnProto => {
- const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", ast_node);
-
- decl.analysis = .in_progress;
-
- // This arena allocator's memory is discarded at the end of this function. It is used
- // to determine the type of the function, and hence the type of the decl, which is needed
- // to complete the Decl analysis.
- var fn_type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
- defer fn_type_scope_arena.deinit();
- var fn_type_scope: Scope.GenZIR = .{
- .decl = decl,
- .arena = &fn_type_scope_arena.allocator,
- .parent = decl.scope,
- };
- defer fn_type_scope.instructions.deinit(self.gpa);
-
- decl.is_pub = fn_proto.getVisibToken() != null;
- const body_node = fn_proto.getBodyNode() orelse
- return self.failTok(&fn_type_scope.base, fn_proto.fn_token, "TODO implement extern functions", .{});
-
- const param_decls = fn_proto.params();
- const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len);
-
- const fn_src = tree.token_locs[fn_proto.fn_token].start;
- const type_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.type_type),
- });
- const type_type_rl: astgen.ResultLoc = .{ .ty = type_type };
- for (param_decls) |param_decl, i| {
- const param_type_node = switch (param_decl.param_type) {
- .any_type => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement anytype parameter", .{}),
- .type_expr => |node| node,
- };
- param_types[i] = try astgen.expr(self, &fn_type_scope.base, type_type_rl, param_type_node);
- }
- if (fn_proto.getVarArgsToken()) |var_args_token| {
- return self.failTok(&fn_type_scope.base, var_args_token, "TODO implement var args", .{});
- }
- if (fn_proto.getLibName()) |lib_name| {
- return self.failNode(&fn_type_scope.base, lib_name, "TODO implement function library name", .{});
- }
- if (fn_proto.getAlignExpr()) |align_expr| {
- return self.failNode(&fn_type_scope.base, align_expr, "TODO implement function align expression", .{});
- }
- if (fn_proto.getSectionExpr()) |sect_expr| {
- return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{});
- }
- if (fn_proto.getCallconvExpr()) |callconv_expr| {
- return self.failNode(
- &fn_type_scope.base,
- callconv_expr,
- "TODO implement function calling convention expression",
- .{},
- );
- }
- const return_type_expr = switch (fn_proto.return_type) {
- .Explicit => |node| node,
- .InferErrorSet => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement inferred error sets", .{}),
- .Invalid => |tok| return self.failTok(&fn_type_scope.base, tok, "unable to parse return type", .{}),
- };
-
- const return_type_inst = try astgen.expr(self, &fn_type_scope.base, type_type_rl, return_type_expr);
- const fn_type_inst = try astgen.addZIRInst(self, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{
- .return_type = return_type_inst,
- .param_types = param_types,
- }, .{});
-
- // We need the memory for the Type to go into the arena for the Decl
- var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
- errdefer decl_arena.deinit();
- const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
-
- var block_scope: Scope.Block = .{
- .parent = null,
- .func = null,
- .decl = decl,
- .instructions = .{},
- .arena = &decl_arena.allocator,
- .is_comptime = false,
- };
- defer block_scope.instructions.deinit(self.gpa);
-
- const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{
- .instructions = fn_type_scope.instructions.items,
- });
- const new_func = try decl_arena.allocator.create(Fn);
- const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
-
- const fn_zir = blk: {
- // This scope's arena memory is discarded after the ZIR generation
- // pass completes, and semantic analysis of it completes.
- var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
- errdefer gen_scope_arena.deinit();
- var gen_scope: Scope.GenZIR = .{
- .decl = decl,
- .arena = &gen_scope_arena.allocator,
- .parent = decl.scope,
- };
- defer gen_scope.instructions.deinit(self.gpa);
-
- // We need an instruction for each parameter, and they must be first in the body.
- try gen_scope.instructions.resize(self.gpa, fn_proto.params_len);
- var params_scope = &gen_scope.base;
- for (fn_proto.params()) |param, i| {
- const name_token = param.name_token.?;
- const src = tree.token_locs[name_token].start;
- const param_name = tree.tokenSlice(name_token); // TODO: call identifierTokenString
- const arg = try gen_scope_arena.allocator.create(zir.Inst.Arg);
- arg.* = .{
- .base = .{
- .tag = .arg,
- .src = src,
- },
- .positionals = .{
- .name = param_name,
- },
- .kw_args = .{},
- };
- gen_scope.instructions.items[i] = &arg.base;
- const sub_scope = try gen_scope_arena.allocator.create(Scope.LocalVal);
- sub_scope.* = .{
- .parent = params_scope,
- .gen_zir = &gen_scope,
- .name = param_name,
- .inst = &arg.base,
- };
- params_scope = &sub_scope.base;
- }
-
- const body_block = body_node.cast(ast.Node.Block).?;
-
- try astgen.blockExpr(self, params_scope, body_block);
-
- if (gen_scope.instructions.items.len == 0 or
- !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn())
- {
- const src = tree.token_locs[body_block.rbrace].start;
- _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid);
- }
-
- const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR);
- fn_zir.* = .{
- .body = .{
- .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items),
- },
- .arena = gen_scope_arena.state,
- };
- break :blk fn_zir;
- };
-
- new_func.* = .{
- .analysis = .{ .queued = fn_zir },
- .owner_decl = decl,
- };
- fn_payload.* = .{ .func = new_func };
-
- var prev_type_has_bits = false;
- var type_changed = true;
-
- if (decl.typedValueManaged()) |tvm| {
- prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
- type_changed = !tvm.typed_value.ty.eql(fn_type);
-
- tvm.deinit(self.gpa);
- }
-
- decl_arena_state.* = decl_arena.state;
- decl.typed_value = .{
- .most_recent = .{
- .typed_value = .{
- .ty = fn_type,
- .val = Value.initPayload(&fn_payload.base),
- },
- .arena = decl_arena_state,
- },
- };
- decl.analysis = .complete;
- decl.generation = self.generation;
-
- if (fn_type.hasCodeGenBits()) {
- // 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 });
- } else if (prev_type_has_bits) {
- self.bin_file.freeDecl(decl);
- }
-
- if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
- if (tree.token_ids[maybe_export_token] == .Keyword_export) {
- const export_src = tree.token_locs[maybe_export_token].start;
- const name_loc = tree.token_locs[fn_proto.getNameToken().?];
- const name = tree.tokenSliceLoc(name_loc);
- // The scope needs to have the decl in it.
- try self.analyzeExport(&block_scope.base, export_src, name, decl);
- }
- }
- return type_changed;
- },
- .VarDecl => {
- const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node);
-
- decl.analysis = .in_progress;
-
- // We need the memory for the Type to go into the arena for the Decl
- var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
- errdefer decl_arena.deinit();
- const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
-
- var block_scope: Scope.Block = .{
- .parent = null,
- .func = null,
- .decl = decl,
- .instructions = .{},
- .arena = &decl_arena.allocator,
- .is_comptime = true,
- };
- defer block_scope.instructions.deinit(self.gpa);
-
- decl.is_pub = var_decl.getVisibToken() != null;
- const is_extern = blk: {
- const maybe_extern_token = var_decl.getExternExportToken() orelse
- break :blk false;
- if (tree.token_ids[maybe_extern_token] != .Keyword_extern) break :blk false;
- if (var_decl.getInitNode()) |some| {
- return self.failNode(&block_scope.base, some, "extern variables have no initializers", .{});
- }
- break :blk true;
- };
- if (var_decl.getLibName()) |lib_name| {
- assert(is_extern);
- return self.failNode(&block_scope.base, lib_name, "TODO implement function library name", .{});
- }
- const is_mutable = tree.token_ids[var_decl.mut_token] == .Keyword_var;
- const is_threadlocal = if (var_decl.getThreadLocalToken()) |some| blk: {
- if (!is_mutable) {
- return self.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{});
- }
- break :blk true;
- } else false;
- assert(var_decl.getComptimeToken() == null);
- if (var_decl.getAlignNode()) |align_expr| {
- return self.failNode(&block_scope.base, align_expr, "TODO implement function align expression", .{});
- }
- if (var_decl.getSectionNode()) |sect_expr| {
- return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{});
- }
-
- const var_info: struct { ty: Type, val: ?Value } = if (var_decl.getInitNode()) |init_node| vi: {
- var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
- defer gen_scope_arena.deinit();
- var gen_scope: Scope.GenZIR = .{
- .decl = decl,
- .arena = &gen_scope_arena.allocator,
- .parent = decl.scope,
- };
- defer gen_scope.instructions.deinit(self.gpa);
-
- const init_result_loc: astgen.ResultLoc = if (var_decl.getTypeNode()) |type_node| rl: {
- const src = tree.token_locs[type_node.firstToken()].start;
- const type_type = try astgen.addZIRInstConst(self, &gen_scope.base, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.type_type),
- });
- const var_type = try astgen.expr(self, &gen_scope.base, .{ .ty = type_type }, type_node);
- break :rl .{ .ty = var_type };
- } else .none;
-
- const src = tree.token_locs[init_node.firstToken()].start;
- const init_inst = try astgen.expr(self, &gen_scope.base, init_result_loc, init_node);
-
- var inner_block: Scope.Block = .{
- .parent = null,
- .func = null,
- .decl = decl,
- .instructions = .{},
- .arena = &gen_scope_arena.allocator,
- .is_comptime = true,
- };
- defer inner_block.instructions.deinit(self.gpa);
- try zir_sema.analyzeBody(self, &inner_block.base, .{ .instructions = gen_scope.instructions.items });
-
- // The result location guarantees the type coercion.
- const analyzed_init_inst = init_inst.analyzed_inst.?;
- // The is_comptime in the Scope.Block guarantees the result is comptime-known.
- const val = analyzed_init_inst.value().?;
-
- const ty = try analyzed_init_inst.ty.copy(block_scope.arena);
- break :vi .{
- .ty = ty,
- .val = try val.copy(block_scope.arena),
- };
- } else if (!is_extern) {
- return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{});
- } else if (var_decl.getTypeNode()) |type_node| vi: {
- // Temporary arena for the zir instructions.
- var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
- defer type_scope_arena.deinit();
- var type_scope: Scope.GenZIR = .{
- .decl = decl,
- .arena = &type_scope_arena.allocator,
- .parent = decl.scope,
- };
- defer type_scope.instructions.deinit(self.gpa);
-
- const src = tree.token_locs[type_node.firstToken()].start;
- const type_type = try astgen.addZIRInstConst(self, &type_scope.base, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.type_type),
- });
- const var_type = try astgen.expr(self, &type_scope.base, .{ .ty = type_type }, type_node);
- const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{
- .instructions = type_scope.instructions.items,
- });
- break :vi .{
- .ty = ty,
- .val = null,
- };
- } else {
- return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{});
- };
-
- if (is_mutable and !var_info.ty.isValidVarType(is_extern)) {
- return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_info.ty});
- }
-
- var type_changed = true;
- if (decl.typedValueManaged()) |tvm| {
- type_changed = !tvm.typed_value.ty.eql(var_info.ty);
-
- tvm.deinit(self.gpa);
- }
-
- const new_variable = try decl_arena.allocator.create(Var);
- const var_payload = try decl_arena.allocator.create(Value.Payload.Variable);
- new_variable.* = .{
- .owner_decl = decl,
- .init = var_info.val orelse undefined,
- .is_extern = is_extern,
- .is_mutable = is_mutable,
- .is_threadlocal = is_threadlocal,
- };
- var_payload.* = .{ .variable = new_variable };
-
- decl_arena_state.* = decl_arena.state;
- decl.typed_value = .{
- .most_recent = .{
- .typed_value = .{
- .ty = var_info.ty,
- .val = Value.initPayload(&var_payload.base),
- },
- .arena = decl_arena_state,
- },
- };
- decl.analysis = .complete;
- decl.generation = self.generation;
-
- if (var_decl.getExternExportToken()) |maybe_export_token| {
- if (tree.token_ids[maybe_export_token] == .Keyword_export) {
- const export_src = tree.token_locs[maybe_export_token].start;
- const name_loc = tree.token_locs[var_decl.name_token];
- const name = tree.tokenSliceLoc(name_loc);
- // The scope needs to have the decl in it.
- try self.analyzeExport(&block_scope.base, export_src, name, decl);
- }
- }
- return type_changed;
- },
- .Comptime => {
- const comptime_decl = @fieldParentPtr(ast.Node.Comptime, "base", ast_node);
-
- decl.analysis = .in_progress;
-
- // A comptime decl does not store any value so we can just deinit this arena after analysis is done.
- var analysis_arena = std.heap.ArenaAllocator.init(self.gpa);
- defer analysis_arena.deinit();
- var gen_scope: Scope.GenZIR = .{
- .decl = decl,
- .arena = &analysis_arena.allocator,
- .parent = decl.scope,
- };
- defer gen_scope.instructions.deinit(self.gpa);
-
- _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr);
-
- var block_scope: Scope.Block = .{
- .parent = null,
- .func = null,
- .decl = decl,
- .instructions = .{},
- .arena = &analysis_arena.allocator,
- .is_comptime = true,
- };
- defer block_scope.instructions.deinit(self.gpa);
-
- _ = try zir_sema.analyzeBody(self, &block_scope.base, .{
- .instructions = gen_scope.instructions.items,
- });
-
- decl.analysis = .complete;
- decl.generation = self.generation;
- return true;
- },
- .Use => @panic("TODO usingnamespace decl"),
- else => unreachable,
- }
-}
-
-fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {
- try depender.dependencies.ensureCapacity(self.gpa, depender.dependencies.items().len + 1);
- try dependee.dependants.ensureCapacity(self.gpa, dependee.dependants.items().len + 1);
-
- depender.dependencies.putAssumeCapacity(dependee, {});
- dependee.dependants.putAssumeCapacity(depender, {});
-}
-
-fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
- switch (root_scope.status) {
- .never_loaded, .unloaded_success => {
- try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
-
- const source = try root_scope.getSource(self);
-
- var keep_zir_module = false;
- const zir_module = try self.gpa.create(zir.Module);
- defer if (!keep_zir_module) self.gpa.destroy(zir_module);
-
- zir_module.* = try zir.parse(self.gpa, source);
- defer if (!keep_zir_module) zir_module.deinit(self.gpa);
-
- 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}),
- );
- root_scope.status = .unloaded_parse_failure;
- return error.AnalysisFail;
- }
-
- root_scope.status = .loaded_success;
- root_scope.contents = .{ .module = zir_module };
- keep_zir_module = true;
-
- return zir_module;
- },
-
- .unloaded_parse_failure,
- .unloaded_sema_failure,
- => return error.AnalysisFail,
-
- .loaded_success, .loaded_sema_failure => return root_scope.contents.module,
- }
-}
-
-fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree {
- const tracy = trace(@src());
- defer tracy.end();
-
- const root_scope = container_scope.file_scope;
-
- switch (root_scope.status) {
- .never_loaded, .unloaded_success => {
- try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
-
- const source = try root_scope.getSource(self);
-
- var keep_tree = false;
- const tree = try std.zig.parse(self.gpa, source);
- defer if (!keep_tree) tree.deinit();
-
- if (tree.errors.len != 0) {
- const parse_err = tree.errors[0];
-
- var msg = std.ArrayList(u8).init(self.gpa);
- defer msg.deinit();
-
- try parse_err.render(tree.token_ids, msg.outStream());
- const err_msg = try self.gpa.create(ErrorMsg);
- err_msg.* = .{
- .msg = msg.toOwnedSlice(),
- .byte_offset = tree.token_locs[parse_err.loc()].start,
- };
-
- self.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg);
- root_scope.status = .unloaded_parse_failure;
- return error.AnalysisFail;
- }
-
- root_scope.status = .loaded_success;
- root_scope.contents = .{ .tree = tree };
- keep_tree = true;
-
- return tree;
- },
-
- .unloaded_parse_failure => return error.AnalysisFail,
-
- .loaded_success => return root_scope.contents.tree,
- }
-}
-
-fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- // We may be analyzing it for the first time, or this may be
- // an incremental update. This code handles both cases.
- const tree = try self.getAstTree(container_scope);
- const decls = tree.root_node.decls();
-
- try self.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
- // we know which ones have been deleted.
- var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa);
- defer deleted_decls.deinit();
- try deleted_decls.ensureCapacity(container_scope.decls.items().len);
- for (container_scope.decls.items()) |entry| {
- deleted_decls.putAssumeCapacityNoClobber(entry.key, {});
- }
-
- for (decls) |src_decl, decl_i| {
- if (src_decl.cast(ast.Node.FnProto)) |fn_proto| {
- // We will create a Decl for it regardless of analysis status.
- const name_tok = fn_proto.getNameToken() orelse {
- @panic("TODO missing function name");
- };
-
- const name_loc = tree.token_locs[name_tok];
- const name = tree.tokenSliceLoc(name_loc);
- const name_hash = container_scope.fullyQualifiedNameHash(name);
- const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
- if (self.decl_table.get(name_hash)) |decl| {
- // Update the AST Node index of the decl, even if its contents are unchanged, it may
- // have been re-ordered.
- 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});
- 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) {
- .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 });
- },
- .macho => {
- // TODO Implement for MachO
- },
- .c, .wasm => {},
- }
- }
- } else {
- const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
- 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 });
- }
- }
- }
- } else if (src_decl.castTag(.VarDecl)) |var_decl| {
- const name_loc = tree.token_locs[var_decl.name_token];
- const name = tree.tokenSliceLoc(name_loc);
- const name_hash = container_scope.fullyQualifiedNameHash(name);
- const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
- if (self.decl_table.get(name_hash)) |decl| {
- // Update the AST Node index of the decl, even if its contents are unchanged, it may
- // have been re-ordered.
- 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});
- 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 {
- const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
- 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 });
- }
- }
- }
- } else if (src_decl.castTag(.Comptime)) |comptime_node| {
- const name_index = self.getNextAnonNameIndex();
- const name = try std.fmt.allocPrint(self.gpa, "__comptime_{}", .{name_index});
- defer self.gpa.free(name);
-
- const name_hash = container_scope.fullyQualifiedNameHash(name);
- const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
-
- 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 });
- } else if (src_decl.castTag(.ContainerField)) |container_field| {
- log.err("TODO: analyze container field", .{});
- } else if (src_decl.castTag(.TestDecl)) |test_decl| {
- log.err("TODO: analyze test decl", .{});
- } else if (src_decl.castTag(.Use)) |use_decl| {
- log.err("TODO: analyze usingnamespace decl", .{});
- } else {
- unreachable;
- }
- }
- // Handle explicitly deleted decls from the source code. Not to be confused
- // with when we delete decls because they are no longer referenced.
- for (deleted_decls.items()) |entry| {
- log.debug("noticed '{}' deleted from source\n", .{entry.key.name});
- try self.deleteDecl(entry.key);
- }
-}
-
-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 root_scope.decls.ensureCapacity(self.gpa, src_module.decls.len);
-
- var exports_to_resolve = std.ArrayList(*zir.Decl).init(self.gpa);
- defer exports_to_resolve.deinit();
-
- // Keep track of the decls that we expect to see in this file so that
- // we know which ones have been deleted.
- var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa);
- defer deleted_decls.deinit();
- try deleted_decls.ensureCapacity(self.decl_table.items().len);
- for (self.decl_table.items()) |entry| {
- deleted_decls.putAssumeCapacityNoClobber(entry.value, {});
- }
-
- for (src_module.decls) |src_decl, decl_i| {
- const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name);
- if (self.decl_table.get(name_hash)) |decl| {
- deleted_decls.removeAssertDiscard(decl);
- if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) {
- try self.markOutdatedDecl(decl);
- decl.contents_hash = src_decl.contents_hash;
- }
- } else {
- const new_decl = try self.createNewDecl(
- &root_scope.base,
- src_decl.name,
- decl_i,
- name_hash,
- src_decl.contents_hash,
- );
- root_scope.decls.appendAssumeCapacity(new_decl);
- if (src_decl.inst.cast(zir.Inst.Export)) |export_inst| {
- try exports_to_resolve.append(src_decl);
- }
- }
- }
- for (exports_to_resolve.items) |export_decl| {
- _ = try zir_sema.resolveZirDecl(self, &root_scope.base, export_decl);
- }
- // Handle explicitly deleted decls from the source code. Not to be confused
- // with when we delete decls because they are no longer referenced.
- for (deleted_decls.items()) |entry| {
- log.debug("noticed '{}' deleted from source\n", .{entry.key.name});
- try self.deleteDecl(entry.key);
- }
-}
-
-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
- // not be present in the set, and this does nothing.
- decl.scope.removeDecl(decl);
-
- log.debug("deleting decl '{}'\n", .{decl.name});
- const name_hash = decl.fullyQualifiedNameHash();
- self.decl_table.removeAssertDiscard(name_hash);
- // Remove itself from its dependencies, because we are about to destroy the decl pointer.
- for (decl.dependencies.items()) |entry| {
- const dep = entry.key;
- dep.removeDependant(decl);
- if (dep.dependants.items().len == 0 and !dep.deletion_flag) {
- // We don't recursively perform a deletion here, because during the update,
- // another reference to it may turn up.
- dep.deletion_flag = true;
- self.deletion_set.appendAssumeCapacity(dep);
- }
- }
- // Anything that depends on this deleted decl certainly needs to be re-analyzed.
- for (decl.dependants.items()) |entry| {
- const dep = entry.key;
- dep.removeDependency(decl);
- if (dep.analysis != .outdated) {
- // TODO Move this failure possibility to the top of the function.
- try self.markOutdatedDecl(dep);
- }
- }
- if (self.failed_decls.remove(decl)) |entry| {
- entry.value.destroy(self.gpa);
- }
- self.deleteDeclExports(decl);
- self.bin_file.freeDecl(decl);
- decl.destroy(self.gpa);
-}
-
-/// Delete all the Export objects that are caused by this Decl. Re-analysis of
-/// this Decl will cause them to be re-created (or not).
-fn deleteDeclExports(self: *Module, decl: *Decl) void {
- const kv = self.export_owners.remove(decl) orelse return;
-
- for (kv.value) |exp| {
- if (self.decl_exports.getEntry(exp.exported_decl)) |decl_exports_kv| {
- // Remove exports with owner_decl matching the regenerating decl.
- const list = decl_exports_kv.value;
- var i: usize = 0;
- var new_len = list.len;
- while (i < new_len) {
- if (list[i].owner_decl == decl) {
- mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]);
- new_len -= 1;
- } else {
- i += 1;
- }
- }
- decl_exports_kv.value = self.gpa.shrink(list, new_len);
- if (new_len == 0) {
- self.decl_exports.removeAssertDiscard(exp.exported_decl);
- }
- }
- if (self.bin_file.cast(link.File.Elf)) |elf| {
- elf.deleteExport(exp.link);
- }
- if (self.failed_exports.remove(exp)) |entry| {
- entry.value.destroy(self.gpa);
- }
- _ = self.symbol_exports.remove(exp.options.name);
- self.gpa.free(exp.options.name);
- self.gpa.destroy(exp);
- }
- self.gpa.free(kv.value);
-}
-
-fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- // Use the Decl's arena for function memory.
- var arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);
- defer decl.typed_value.most_recent.arena.?.* = arena.state;
- var inner_block: Scope.Block = .{
- .parent = null,
- .func = func,
- .decl = decl,
- .instructions = .{},
- .arena = &arena.allocator,
- .is_comptime = false,
- };
- defer inner_block.instructions.deinit(self.gpa);
-
- const fn_zir = func.analysis.queued;
- defer fn_zir.arena.promote(self.gpa).deinit();
- func.analysis = .{ .in_progress = {} };
- log.debug("set {} to in_progress\n", .{decl.name});
-
- try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body);
-
- const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
- func.analysis = .{ .success = .{ .instructions = instructions } };
- log.debug("set {} to success\n", .{decl.name});
-}
-
-fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
- log.debug("mark {} outdated\n", .{decl.name});
- try self.work_queue.writeItem(.{ .analyze_decl = decl });
- if (self.failed_decls.remove(decl)) |entry| {
- entry.value.destroy(self.gpa);
- }
- decl.analysis = .outdated;
-}
-
-fn allocateNewDecl(
- self: *Module,
- scope: *Scope,
- src_index: usize,
- contents_hash: std.zig.SrcHash,
-) !*Decl {
- const new_decl = try self.gpa.create(Decl);
- new_decl.* = .{
- .name = "",
- .scope = scope.namespace(),
- .src_index = src_index,
- .typed_value = .{ .never_succeeded = {} },
- .analysis = .unreferenced,
- .deletion_flag = false,
- .contents_hash = contents_hash,
- .link = switch (self.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) {
- .coff => .{ .coff = {} },
- .elf => .{ .elf = link.File.Elf.SrcFn.empty },
- .macho => .{ .macho = link.File.MachO.SrcFn.empty },
- .c => .{ .c = {} },
- .wasm => .{ .wasm = null },
- },
- .generation = 0,
- .is_pub = false,
- };
- return new_decl;
-}
-
-fn createNewDecl(
- self: *Module,
- scope: *Scope,
- decl_name: []const u8,
- src_index: usize,
- name_hash: Scope.NameHash,
- contents_hash: std.zig.SrcHash,
-) !*Decl {
- try self.decl_table.ensureCapacity(self.gpa, self.decl_table.items().len + 1);
- const new_decl = try self.allocateNewDecl(scope, src_index, contents_hash);
- errdefer self.gpa.destroy(new_decl);
- new_decl.name = try mem.dupeZ(self.gpa, u8, decl_name);
- self.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl);
- return new_decl;
-}
-
-/// Get error value for error tag `name`.
-pub fn getErrorValue(self: *Module, name: []const u8) !std.StringHashMapUnmanaged(u16).Entry {
- const gop = try self.global_error_set.getOrPut(self.gpa, name);
- if (gop.found_existing)
- return gop.entry.*;
- errdefer self.global_error_set.removeAssertDiscard(name);
-
- gop.entry.key = try self.gpa.dupe(u8, name);
- gop.entry.value = @intCast(u16, self.global_error_set.count() - 1);
- return gop.entry.*;
-}
-
-pub fn requireFunctionBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
- return scope.cast(Scope.Block) orelse
- return self.fail(scope, src, "instruction illegal outside function body", .{});
-}
-
-pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
- const block = try self.requireFunctionBlock(scope, src);
- if (block.is_comptime) {
- return self.fail(scope, src, "unable to resolve comptime value", .{});
- }
- return block;
-}
-
-pub fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value {
- return (try self.resolveDefinedValue(scope, base)) orelse
- return self.fail(scope, base.src, "unable to resolve comptime value", .{});
-}
-
-pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
- if (base.value()) |val| {
- if (val.isUndef()) {
- return self.fail(scope, base.src, "use of undefined value here causes undefined behavior", .{});
- }
- return val;
- }
- return null;
-}
-
-pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_name: []const u8, exported_decl: *Decl) !void {
- try self.ensureDeclAnalyzed(exported_decl);
- const typed_value = exported_decl.typed_value.most_recent.typed_value;
- switch (typed_value.ty.zigTypeTag()) {
- .Fn => {},
- else => return self.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}),
- }
-
- try self.decl_exports.ensureCapacity(self.gpa, self.decl_exports.items().len + 1);
- try self.export_owners.ensureCapacity(self.gpa, self.export_owners.items().len + 1);
-
- const new_export = try self.gpa.create(Export);
- errdefer self.gpa.destroy(new_export);
-
- const symbol_name = try self.gpa.dupe(u8, borrowed_symbol_name);
- errdefer self.gpa.free(symbol_name);
-
- const owner_decl = scope.decl().?;
-
- new_export.* = .{
- .options = .{ .name = symbol_name },
- .src = src,
- .link = .{},
- .owner_decl = owner_decl,
- .exported_decl = exported_decl,
- .status = .in_progress,
- };
-
- // Add to export_owners table.
- const eo_gop = self.export_owners.getOrPutAssumeCapacity(owner_decl);
- if (!eo_gop.found_existing) {
- eo_gop.entry.value = &[0]*Export{};
- }
- eo_gop.entry.value = try self.gpa.realloc(eo_gop.entry.value, eo_gop.entry.value.len + 1);
- eo_gop.entry.value[eo_gop.entry.value.len - 1] = new_export;
- errdefer eo_gop.entry.value = self.gpa.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1);
-
- // Add to exported_decl table.
- const de_gop = self.decl_exports.getOrPutAssumeCapacity(exported_decl);
- if (!de_gop.found_existing) {
- de_gop.entry.value = &[0]*Export{};
- }
- de_gop.entry.value = try self.gpa.realloc(de_gop.entry.value, de_gop.entry.value.len + 1);
- de_gop.entry.value[de_gop.entry.value.len - 1] = new_export;
- errdefer de_gop.entry.value = self.gpa.shrink(de_gop.entry.value, de_gop.entry.value.len - 1);
-
- 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.gpa,
- src,
- "exported symbol collision: {}",
- .{symbol_name},
- ));
- // TODO: add a note
- new_export.status = .failed;
- return;
- }
-
- 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) {
- 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.gpa,
- src,
- "unable to export: {}",
- .{@errorName(err)},
- ));
- new_export.status = .failed_retryable;
- },
- };
-}
-
-pub fn addNoOp(
- self: *Module,
- block: *Scope.Block,
- src: usize,
- ty: Type,
- comptime tag: Inst.Tag,
-) !*Inst {
- const inst = try block.arena.create(tag.Type());
- inst.* = .{
- .base = .{
- .tag = tag,
- .ty = ty,
- .src = src,
- },
- };
- try block.instructions.append(self.gpa, &inst.base);
- return &inst.base;
-}
-
-pub fn addUnOp(
- self: *Module,
- block: *Scope.Block,
- src: usize,
- ty: Type,
- tag: Inst.Tag,
- operand: *Inst,
-) !*Inst {
- const inst = try block.arena.create(Inst.UnOp);
- inst.* = .{
- .base = .{
- .tag = tag,
- .ty = ty,
- .src = src,
- },
- .operand = operand,
- };
- try block.instructions.append(self.gpa, &inst.base);
- return &inst.base;
-}
-
-pub fn addBinOp(
- self: *Module,
- block: *Scope.Block,
- src: usize,
- ty: Type,
- tag: Inst.Tag,
- lhs: *Inst,
- rhs: *Inst,
-) !*Inst {
- const inst = try block.arena.create(Inst.BinOp);
- inst.* = .{
- .base = .{
- .tag = tag,
- .ty = ty,
- .src = src,
- },
- .lhs = lhs,
- .rhs = rhs,
- };
- try block.instructions.append(self.gpa, &inst.base);
- return &inst.base;
-}
-
-pub fn addArg(self: *Module, block: *Scope.Block, src: usize, ty: Type, name: [*:0]const u8) !*Inst {
- const inst = try block.arena.create(Inst.Arg);
- inst.* = .{
- .base = .{
- .tag = .arg,
- .ty = ty,
- .src = src,
- },
- .name = name,
- };
- try block.instructions.append(self.gpa, &inst.base);
- return &inst.base;
-}
-
-pub fn addBr(
- self: *Module,
- scope_block: *Scope.Block,
- src: usize,
- target_block: *Inst.Block,
- operand: *Inst,
-) !*Inst {
- const inst = try scope_block.arena.create(Inst.Br);
- inst.* = .{
- .base = .{
- .tag = .br,
- .ty = Type.initTag(.noreturn),
- .src = src,
- },
- .operand = operand,
- .block = target_block,
- };
- try scope_block.instructions.append(self.gpa, &inst.base);
- return &inst.base;
-}
-
-pub fn addCondBr(
- self: *Module,
- block: *Scope.Block,
- src: usize,
- condition: *Inst,
- then_body: ir.Body,
- else_body: ir.Body,
-) !*Inst {
- const inst = try block.arena.create(Inst.CondBr);
- inst.* = .{
- .base = .{
- .tag = .condbr,
- .ty = Type.initTag(.noreturn),
- .src = src,
- },
- .condition = condition,
- .then_body = then_body,
- .else_body = else_body,
- };
- try block.instructions.append(self.gpa, &inst.base);
- return &inst.base;
-}
-
-pub fn addCall(
- self: *Module,
- block: *Scope.Block,
- src: usize,
- ty: Type,
- func: *Inst,
- args: []const *Inst,
-) !*Inst {
- const inst = try block.arena.create(Inst.Call);
- inst.* = .{
- .base = .{
- .tag = .call,
- .ty = ty,
- .src = src,
- },
- .func = func,
- .args = args,
- };
- try block.instructions.append(self.gpa, &inst.base);
- return &inst.base;
-}
-
-pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst {
- const const_inst = try scope.arena().create(Inst.Constant);
- const_inst.* = .{
- .base = .{
- .tag = Inst.Constant.base_tag,
- .ty = typed_value.ty,
- .src = src,
- },
- .val = typed_value.val,
- };
- return &const_inst.base;
-}
-
-pub fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
- return self.constInst(scope, src, .{
- .ty = Type.initTag(.type),
- .val = try ty.toValue(scope.arena()),
- });
-}
-
-pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {
- return self.constInst(scope, src, .{
- .ty = Type.initTag(.void),
- .val = Value.initTag(.void_value),
- });
-}
-
-pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst {
- return self.constInst(scope, src, .{
- .ty = Type.initTag(.noreturn),
- .val = Value.initTag(.unreachable_value),
- });
-}
-
-pub fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
- return self.constInst(scope, src, .{
- .ty = ty,
- .val = Value.initTag(.undef),
- });
-}
-
-pub fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst {
- return self.constInst(scope, src, .{
- .ty = Type.initTag(.bool),
- .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)],
- });
-}
-
-pub fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64) !*Inst {
- const int_payload = try scope.arena().create(Value.Payload.Int_u64);
- int_payload.* = .{ .int = int };
-
- return self.constInst(scope, src, .{
- .ty = ty,
- .val = Value.initPayload(&int_payload.base),
- });
-}
-
-pub fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) !*Inst {
- const int_payload = try scope.arena().create(Value.Payload.Int_i64);
- int_payload.* = .{ .int = int };
-
- return self.constInst(scope, src, .{
- .ty = ty,
- .val = Value.initPayload(&int_payload.base),
- });
-}
-
-pub fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigIntConst) !*Inst {
- const val_payload = if (big_int.positive) blk: {
- if (big_int.to(u64)) |x| {
- return self.constIntUnsigned(scope, src, ty, x);
- } else |err| switch (err) {
- error.NegativeIntoUnsigned => unreachable,
- error.TargetTooSmall => {}, // handled below
- }
- const big_int_payload = try scope.arena().create(Value.Payload.IntBigPositive);
- big_int_payload.* = .{ .limbs = big_int.limbs };
- break :blk &big_int_payload.base;
- } else blk: {
- if (big_int.to(i64)) |x| {
- return self.constIntSigned(scope, src, ty, x);
- } else |err| switch (err) {
- error.NegativeIntoUnsigned => unreachable,
- error.TargetTooSmall => {}, // handled below
- }
- const big_int_payload = try scope.arena().create(Value.Payload.IntBigNegative);
- big_int_payload.* = .{ .limbs = big_int.limbs };
- break :blk &big_int_payload.base;
- };
-
- return self.constInst(scope, src, .{
- .ty = ty,
- .val = Value.initPayload(val_payload),
- });
-}
-
-pub fn createAnonymousDecl(
- self: *Module,
- scope: *Scope,
- decl_arena: *std.heap.ArenaAllocator,
- typed_value: TypedValue,
-) !*Decl {
- const name_index = self.getNextAnonNameIndex();
- const scope_decl = scope.decl().?;
- const name = try std.fmt.allocPrint(self.gpa, "{}__anon_{}", .{ scope_decl.name, name_index });
- defer self.gpa.free(name);
- const name_hash = scope.namespace().fullyQualifiedNameHash(name);
- const src_hash: std.zig.SrcHash = undefined;
- const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash);
- const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
-
- decl_arena_state.* = decl_arena.state;
- new_decl.typed_value = .{
- .most_recent = .{
- .typed_value = typed_value,
- .arena = decl_arena_state,
- },
- };
- new_decl.analysis = .complete;
- new_decl.generation = self.generation;
-
- // TODO: This generates the Decl into the machine code file if it is of a type that is non-zero size.
- // 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 });
- }
-
- return new_decl;
-}
-
-fn getNextAnonNameIndex(self: *Module) usize {
- return @atomicRmw(usize, &self.next_anon_name_index, .Add, 1, .Monotonic);
-}
-
-pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl {
- const namespace = scope.namespace();
- const name_hash = namespace.fullyQualifiedNameHash(ident_name);
- return self.decl_table.get(name_hash);
-}
-
-pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
- const scope_decl = scope.decl().?;
- try self.declareDeclDependency(scope_decl, decl);
- self.ensureDeclAnalyzed(decl) catch |err| {
- if (scope.cast(Scope.Block)) |block| {
- if (block.func) |func| {
- func.analysis = .dependency_failure;
- } else {
- block.decl.analysis = .dependency_failure;
- }
- } else {
- scope_decl.analysis = .dependency_failure;
- }
- return err;
- };
-
- const decl_tv = try decl.typedValue();
- if (decl_tv.val.tag() == .variable) {
- return self.analyzeVarRef(scope, src, decl_tv);
- }
- const ty = try self.simplePtrType(scope, src, decl_tv.ty, false, .One);
- const val_payload = try scope.arena().create(Value.Payload.DeclRef);
- val_payload.* = .{ .decl = decl };
-
- return self.constInst(scope, src, .{
- .ty = ty,
- .val = Value.initPayload(&val_payload.base),
- });
-}
-
-fn analyzeVarRef(self: *Module, scope: *Scope, src: usize, tv: TypedValue) InnerError!*Inst {
- const variable = tv.val.cast(Value.Payload.Variable).?.variable;
-
- const ty = try self.simplePtrType(scope, src, tv.ty, variable.is_mutable, .One);
- if (!variable.is_mutable and !variable.is_extern) {
- const val_payload = try scope.arena().create(Value.Payload.RefVal);
- val_payload.* = .{ .val = variable.init };
- return self.constInst(scope, src, .{
- .ty = ty,
- .val = Value.initPayload(&val_payload.base),
- });
- }
-
- const b = try self.requireRuntimeBlock(scope, src);
- const inst = try b.arena.create(Inst.VarPtr);
- inst.* = .{
- .base = .{
- .tag = .varptr,
- .ty = ty,
- .src = src,
- },
- .variable = variable,
- };
- try b.instructions.append(self.gpa, &inst.base);
- return &inst.base;
-}
-
-pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst {
- const elem_ty = switch (ptr.ty.zigTypeTag()) {
- .Pointer => ptr.ty.elemType(),
- else => return self.fail(scope, ptr_src, "expected pointer, found '{}'", .{ptr.ty}),
- };
- if (ptr.value()) |val| {
- return self.constInst(scope, src, .{
- .ty = elem_ty,
- .val = try val.pointerDeref(scope.arena()),
- });
- }
-
- const b = try self.requireRuntimeBlock(scope, src);
- return self.addUnOp(b, src, elem_ty, .load, ptr);
-}
-
-pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst {
- const decl = self.lookupDeclName(scope, decl_name) orelse
- return self.fail(scope, src, "decl '{}' not found", .{decl_name});
- return self.analyzeDeclRef(scope, src, decl);
-}
-
-pub fn wantSafety(self: *Module, scope: *Scope) bool {
- // TODO take into account scope's safety overrides
- return switch (self.optimizeMode()) {
- .Debug => true,
- .ReleaseSafe => true,
- .ReleaseFast => false,
- .ReleaseSmall => false,
- };
-}
-
-pub fn analyzeIsNull(
- self: *Module,
- scope: *Scope,
- src: usize,
- operand: *Inst,
- invert_logic: bool,
-) InnerError!*Inst {
- if (operand.value()) |opt_val| {
- const is_null = opt_val.isNull();
- const bool_value = if (invert_logic) !is_null else is_null;
- return self.constBool(scope, src, bool_value);
- }
- const b = try self.requireRuntimeBlock(scope, src);
- const inst_tag: Inst.Tag = if (invert_logic) .isnonnull else .isnull;
- return self.addUnOp(b, src, Type.initTag(.bool), inst_tag, operand);
-}
-
-pub fn analyzeIsErr(self: *Module, scope: *Scope, src: usize, operand: *Inst) InnerError!*Inst {
- return self.fail(scope, src, "TODO implement analysis of iserr", .{});
-}
-
-pub fn analyzeSlice(self: *Module, scope: *Scope, src: usize, array_ptr: *Inst, start: *Inst, end_opt: ?*Inst, sentinel_opt: ?*Inst) InnerError!*Inst {
- const ptr_child = switch (array_ptr.ty.zigTypeTag()) {
- .Pointer => array_ptr.ty.elemType(),
- else => return self.fail(scope, src, "expected pointer, found '{}'", .{array_ptr.ty}),
- };
-
- var array_type = ptr_child;
- const elem_type = switch (ptr_child.zigTypeTag()) {
- .Array => ptr_child.elemType(),
- .Pointer => blk: {
- if (ptr_child.isSinglePointer()) {
- if (ptr_child.elemType().zigTypeTag() == .Array) {
- array_type = ptr_child.elemType();
- break :blk ptr_child.elemType().elemType();
- }
-
- return self.fail(scope, src, "slice of single-item pointer", .{});
- }
- break :blk ptr_child.elemType();
- },
- else => return self.fail(scope, src, "slice of non-array type '{}'", .{ptr_child}),
- };
-
- const slice_sentinel = if (sentinel_opt) |sentinel| blk: {
- const casted = try self.coerce(scope, elem_type, sentinel);
- break :blk try self.resolveConstValue(scope, casted);
- } else null;
-
- var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice;
- var return_elem_type = elem_type;
- if (end_opt) |end| {
- if (end.value()) |end_val| {
- if (start.value()) |start_val| {
- const start_u64 = start_val.toUnsignedInt();
- const end_u64 = end_val.toUnsignedInt();
- if (start_u64 > end_u64) {
- return self.fail(scope, src, "out of bounds slice", .{});
- }
-
- const len = end_u64 - start_u64;
- const array_sentinel = if (array_type.zigTypeTag() == .Array and end_u64 == array_type.arrayLen())
- array_type.sentinel()
- else
- slice_sentinel;
- return_elem_type = try self.arrayType(scope, len, array_sentinel, elem_type);
- return_ptr_size = .One;
- }
- }
- }
- const return_type = try self.ptrType(
- scope,
- src,
- return_elem_type,
- if (end_opt == null) slice_sentinel else null,
- 0, // TODO alignment
- 0,
- 0,
- !ptr_child.isConstPtr(),
- ptr_child.isAllowzeroPtr(),
- ptr_child.isVolatilePtr(),
- return_ptr_size,
- );
-
- return self.fail(scope, src, "TODO implement analysis of slice", .{});
-}
-
-/// Asserts that lhs and rhs types are both numeric.
-pub fn cmpNumeric(
- self: *Module,
- scope: *Scope,
- src: usize,
- lhs: *Inst,
- rhs: *Inst,
- op: std.math.CompareOperator,
-) !*Inst {
- assert(lhs.ty.isNumeric());
- assert(rhs.ty.isNumeric());
-
- const lhs_ty_tag = lhs.ty.zigTypeTag();
- const rhs_ty_tag = rhs.ty.zigTypeTag();
-
- if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) {
- if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
- return self.fail(scope, src, "vector length mismatch: {} and {}", .{
- lhs.ty.arrayLen(),
- rhs.ty.arrayLen(),
- });
- }
- return self.fail(scope, src, "TODO implement support for vectors in cmpNumeric", .{});
- } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) {
- return self.fail(scope, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
- lhs.ty,
- rhs.ty,
- });
- }
-
- if (lhs.value()) |lhs_val| {
- if (rhs.value()) |rhs_val| {
- return self.constBool(scope, src, Value.compare(lhs_val, op, rhs_val));
- }
- }
-
- // TODO handle comparisons against lazy zero values
- // Some values can be compared against zero without being runtime known or without forcing
- // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to
- // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout
- // of this function if we don't need to.
-
- // It must be a runtime comparison.
- const b = try self.requireRuntimeBlock(scope, src);
- // For floats, emit a float comparison instruction.
- const lhs_is_float = switch (lhs_ty_tag) {
- .Float, .ComptimeFloat => true,
- else => false,
- };
- const rhs_is_float = switch (rhs_ty_tag) {
- .Float, .ComptimeFloat => true,
- else => false,
- };
- if (lhs_is_float and rhs_is_float) {
- // Implicit cast the smaller one to the larger one.
- const dest_type = x: {
- if (lhs_ty_tag == .ComptimeFloat) {
- break :x rhs.ty;
- } else if (rhs_ty_tag == .ComptimeFloat) {
- break :x lhs.ty;
- }
- if (lhs.ty.floatBits(self.target()) >= rhs.ty.floatBits(self.target())) {
- break :x lhs.ty;
- } else {
- break :x rhs.ty;
- }
- };
- const casted_lhs = try self.coerce(scope, dest_type, lhs);
- const casted_rhs = try self.coerce(scope, dest_type, rhs);
- return self.addBinOp(b, src, dest_type, Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
- }
- // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
- // For mixed signed and unsigned integers, implicit cast both operands to a signed
- // integer with + 1 bit.
- // For mixed floats and integers, extract the integer part from the float, cast that to
- // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
- // add/subtract 1.
- const lhs_is_signed = if (lhs.value()) |lhs_val|
- lhs_val.compareWithZero(.lt)
- else
- (lhs.ty.isFloat() or lhs.ty.isSignedInt());
- const rhs_is_signed = if (rhs.value()) |rhs_val|
- rhs_val.compareWithZero(.lt)
- else
- (rhs.ty.isFloat() or rhs.ty.isSignedInt());
- const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
-
- var dest_float_type: ?Type = null;
-
- var lhs_bits: usize = undefined;
- if (lhs.value()) |lhs_val| {
- if (lhs_val.isUndef())
- return self.constUndef(scope, src, Type.initTag(.bool));
- const is_unsigned = if (lhs_is_float) x: {
- var bigint_space: Value.BigIntSpace = undefined;
- var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(self.gpa);
- defer bigint.deinit();
- const zcmp = lhs_val.orderAgainstZero();
- if (lhs_val.floatHasFraction()) {
- switch (op) {
- .eq => return self.constBool(scope, src, false),
- .neq => return self.constBool(scope, src, true),
- else => {},
- }
- if (zcmp == .lt) {
- try bigint.addScalar(bigint.toConst(), -1);
- } else {
- try bigint.addScalar(bigint.toConst(), 1);
- }
- }
- lhs_bits = bigint.toConst().bitCountTwosComp();
- break :x (zcmp != .lt);
- } else x: {
- lhs_bits = lhs_val.intBitCountTwosComp();
- break :x (lhs_val.orderAgainstZero() != .lt);
- };
- lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
- } else if (lhs_is_float) {
- dest_float_type = lhs.ty;
- } else {
- const int_info = lhs.ty.intInfo(self.target());
- lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
- }
-
- var rhs_bits: usize = undefined;
- if (rhs.value()) |rhs_val| {
- if (rhs_val.isUndef())
- return self.constUndef(scope, src, Type.initTag(.bool));
- const is_unsigned = if (rhs_is_float) x: {
- var bigint_space: Value.BigIntSpace = undefined;
- var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(self.gpa);
- defer bigint.deinit();
- const zcmp = rhs_val.orderAgainstZero();
- if (rhs_val.floatHasFraction()) {
- switch (op) {
- .eq => return self.constBool(scope, src, false),
- .neq => return self.constBool(scope, src, true),
- else => {},
- }
- if (zcmp == .lt) {
- try bigint.addScalar(bigint.toConst(), -1);
- } else {
- try bigint.addScalar(bigint.toConst(), 1);
- }
- }
- rhs_bits = bigint.toConst().bitCountTwosComp();
- break :x (zcmp != .lt);
- } else x: {
- rhs_bits = rhs_val.intBitCountTwosComp();
- break :x (rhs_val.orderAgainstZero() != .lt);
- };
- rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
- } else if (rhs_is_float) {
- dest_float_type = rhs.ty;
- } else {
- const int_info = rhs.ty.intInfo(self.target());
- rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
- }
-
- const dest_type = if (dest_float_type) |ft| ft else blk: {
- const max_bits = std.math.max(lhs_bits, rhs_bits);
- const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) {
- error.Overflow => return self.fail(scope, src, "{} exceeds maximum integer bit count", .{max_bits}),
- };
- break :blk try self.makeIntType(scope, dest_int_is_signed, casted_bits);
- };
- const casted_lhs = try self.coerce(scope, dest_type, lhs);
- const casted_rhs = try self.coerce(scope, dest_type, rhs);
-
- return self.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
-}
-
-fn wrapOptional(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
- if (inst.value()) |val| {
- return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
- }
-
- const b = try self.requireRuntimeBlock(scope, inst.src);
- return self.addUnOp(b, inst.src, dest_type, .wrap_optional, inst);
-}
-
-fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
- if (signed) {
- const int_payload = try scope.arena().create(Type.Payload.IntSigned);
- int_payload.* = .{ .bits = bits };
- return Type.initPayload(&int_payload.base);
- } else {
- const int_payload = try scope.arena().create(Type.Payload.IntUnsigned);
- int_payload.* = .{ .bits = bits };
- return Type.initPayload(&int_payload.base);
- }
-}
-
-pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {
- if (instructions.len == 0)
- return Type.initTag(.noreturn);
-
- if (instructions.len == 1)
- return instructions[0].ty;
-
- var prev_inst = instructions[0];
- for (instructions[1..]) |next_inst| {
- if (next_inst.ty.eql(prev_inst.ty))
- continue;
- if (next_inst.ty.zigTypeTag() == .NoReturn)
- continue;
- if (prev_inst.ty.zigTypeTag() == .NoReturn) {
- prev_inst = next_inst;
- continue;
- }
- if (next_inst.ty.zigTypeTag() == .Undefined)
- continue;
- if (prev_inst.ty.zigTypeTag() == .Undefined) {
- prev_inst = next_inst;
- continue;
- }
- if (prev_inst.ty.isInt() and
- 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) {
- 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())) {
- prev_inst = next_inst;
- }
- continue;
- }
-
- // TODO error notes pointing out each type
- return self.fail(scope, next_inst.src, "incompatible types: '{}' and '{}'", .{ prev_inst.ty, next_inst.ty });
- }
-
- return prev_inst.ty;
-}
-
-pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
- // If the types are the same, we can return the operand.
- if (dest_type.eql(inst.ty))
- return inst;
-
- const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
- if (in_memory_result == .ok) {
- return self.bitcast(scope, dest_type, inst);
- }
-
- // undefined to anything
- if (inst.value()) |val| {
- if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) {
- return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
- }
- }
- assert(inst.ty.zigTypeTag() != .Undefined);
-
- // null to ?T
- if (dest_type.zigTypeTag() == .Optional and inst.ty.zigTypeTag() == .Null) {
- return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });
- }
-
- // T to ?T
- if (dest_type.zigTypeTag() == .Optional) {
- var buf: Type.Payload.PointerSimple = undefined;
- const child_type = dest_type.optionalChild(&buf);
- if (child_type.eql(inst.ty)) {
- return self.wrapOptional(scope, dest_type, inst);
- } else if (try self.coerceNum(scope, child_type, inst)) |some| {
- return self.wrapOptional(scope, dest_type, some);
- }
- }
-
- // *[N]T to []T
- if (inst.ty.isSinglePointer() and dest_type.isSlice() and
- (!inst.ty.isConstPtr() or dest_type.isConstPtr()))
- {
- const array_type = inst.ty.elemType();
- const dst_elem_type = dest_type.elemType();
- if (array_type.zigTypeTag() == .Array and
- coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok)
- {
- return self.coerceArrayPtrToSlice(scope, dest_type, inst);
- }
- }
-
- // comptime known number to other number
- if (try self.coerceNum(scope, dest_type, inst)) |some|
- return some;
-
- // integer widening
- 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());
- 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
- (src_info.signed and !dst_info.signed and dst_info.bits > src_info.bits))
- {
- const b = try self.requireRuntimeBlock(scope, inst.src);
- return self.addUnOp(b, inst.src, dest_type, .intcast, inst);
- }
- }
-
- // float widening
- 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());
- if (dst_bits >= src_bits) {
- const b = try self.requireRuntimeBlock(scope, inst.src);
- return self.addUnOp(b, inst.src, dest_type, .floatcast, inst);
- }
- }
-
- return self.fail(scope, inst.src, "expected {}, found {}", .{ dest_type, inst.ty });
-}
-
-pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !?*Inst {
- const val = inst.value() orelse return null;
- const src_zig_tag = inst.ty.zigTypeTag();
- const dst_zig_tag = dest_type.zigTypeTag();
-
- if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {
- if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
- if (val.floatHasFraction()) {
- return self.fail(scope, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty });
- }
- 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())) {
- 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) {
- error.Overflow => return self.fail(
- scope,
- inst.src,
- "cast of value {} to type '{}' loses information",
- .{ val, dest_type },
- ),
- error.OutOfMemory => return error.OutOfMemory,
- };
- return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = res });
- } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
- return self.fail(scope, inst.src, "TODO int to float", .{});
- }
- }
- return null;
-}
-
-pub fn storePtr(self: *Module, scope: *Scope, src: usize, ptr: *Inst, uncasted_value: *Inst) !*Inst {
- if (ptr.ty.isConstPtr())
- return self.fail(scope, src, "cannot assign to constant", .{});
-
- const elem_ty = ptr.ty.elemType();
- const value = try self.coerce(scope, elem_ty, uncasted_value);
- if (elem_ty.onePossibleValue() != null)
- return self.constVoid(scope, src);
-
- // TODO handle comptime pointer writes
- // TODO handle if the element type requires comptime
-
- const b = try self.requireRuntimeBlock(scope, src);
- return self.addBinOp(b, src, Type.initTag(.void), .store, ptr, value);
-}
-
-pub fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
- if (inst.value()) |val| {
- // Keep the comptime Value representation; take the new type.
- return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
- }
- // TODO validate the type size and other compile errors
- const b = try self.requireRuntimeBlock(scope, inst.src);
- return self.addUnOp(b, inst.src, dest_type, .bitcast, inst);
-}
-
-fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
- if (inst.value()) |val| {
- // The comptime Value representation is compatible with both types.
- return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
- }
- return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
-}
-
-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);
- return self.failWithOwnedErrorMsg(scope, src, err_msg);
-}
-
-pub fn failTok(
- self: *Module,
- scope: *Scope,
- token_index: ast.TokenIndex,
- comptime format: []const u8,
- args: anytype,
-) InnerError {
- @setCold(true);
- const src = scope.tree().token_locs[token_index].start;
- return self.fail(scope, src, format, args);
-}
-
-pub fn failNode(
- self: *Module,
- scope: *Scope,
- ast_node: *ast.Node,
- comptime format: []const u8,
- args: anytype,
-) InnerError {
- @setCold(true);
- const src = scope.tree().token_locs[ast_node.firstToken()].start;
- return self.fail(scope, src, format, args);
-}
-
-fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *ErrorMsg) InnerError {
- {
- errdefer err_msg.destroy(self.gpa);
- try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
- try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
- }
- switch (scope.tag) {
- .decl => {
- const decl = scope.cast(Scope.DeclAnalysis).?.decl;
- decl.analysis = .sema_failure;
- decl.generation = self.generation;
- self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg);
- },
- .block => {
- const block = scope.cast(Scope.Block).?;
- if (block.func) |func| {
- func.analysis = .sema_failure;
- } else {
- block.decl.analysis = .sema_failure;
- block.decl.generation = self.generation;
- }
- self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg);
- },
- .gen_zir => {
- const gen_zir = scope.cast(Scope.GenZIR).?;
- gen_zir.decl.analysis = .sema_failure;
- gen_zir.decl.generation = self.generation;
- self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
- },
- .local_val => {
- const gen_zir = scope.cast(Scope.LocalVal).?.gen_zir;
- gen_zir.decl.analysis = .sema_failure;
- gen_zir.decl.generation = self.generation;
- self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
- },
- .local_ptr => {
- const gen_zir = scope.cast(Scope.LocalPtr).?.gen_zir;
- gen_zir.decl.analysis = .sema_failure;
- gen_zir.decl.generation = self.generation;
- self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
- },
- .zir_module => {
- const zir_module = scope.cast(Scope.ZIRModule).?;
- zir_module.status = .loaded_sema_failure;
- self.failed_files.putAssumeCapacityNoClobber(scope, err_msg);
- },
- .file => unreachable,
- .container => unreachable,
- }
- return error.AnalysisFail;
-}
-
-const InMemoryCoercionResult = enum {
- ok,
- no_match,
-};
-
-fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult {
- if (dest_type.eql(src_type))
- return .ok;
-
- // TODO: implement more of this function
-
- 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);
-}
-
-pub fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
- // TODO is this a performance issue? maybe we should try the operation without
- // resorting to BigInt first.
- var lhs_space: Value.BigIntSpace = undefined;
- var rhs_space: Value.BigIntSpace = undefined;
- const lhs_bigint = lhs.toBigInt(&lhs_space);
- const rhs_bigint = rhs.toBigInt(&rhs_space);
- const limbs = try allocator.alloc(
- std.math.big.Limb,
- std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
- );
- var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
- result_bigint.add(lhs_bigint, rhs_bigint);
- const result_limbs = result_bigint.limbs[0..result_bigint.len];
-
- const val_payload = if (result_bigint.positive) blk: {
- const val_payload = try allocator.create(Value.Payload.IntBigPositive);
- val_payload.* = .{ .limbs = result_limbs };
- break :blk &val_payload.base;
- } else blk: {
- const val_payload = try allocator.create(Value.Payload.IntBigNegative);
- val_payload.* = .{ .limbs = result_limbs };
- break :blk &val_payload.base;
- };
-
- return Value.initPayload(val_payload);
-}
-
-pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
- // TODO is this a performance issue? maybe we should try the operation without
- // resorting to BigInt first.
- var lhs_space: Value.BigIntSpace = undefined;
- var rhs_space: Value.BigIntSpace = undefined;
- const lhs_bigint = lhs.toBigInt(&lhs_space);
- const rhs_bigint = rhs.toBigInt(&rhs_space);
- const limbs = try allocator.alloc(
- std.math.big.Limb,
- std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
- );
- var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
- result_bigint.sub(lhs_bigint, rhs_bigint);
- const result_limbs = result_bigint.limbs[0..result_bigint.len];
-
- const val_payload = if (result_bigint.positive) blk: {
- const val_payload = try allocator.create(Value.Payload.IntBigPositive);
- val_payload.* = .{ .limbs = result_limbs };
- break :blk &val_payload.base;
- } else blk: {
- const val_payload = try allocator.create(Value.Payload.IntBigNegative);
- val_payload.* = .{ .limbs = result_limbs };
- break :blk &val_payload.base;
- };
-
- return Value.initPayload(val_payload);
-}
-
-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()),
- };
-
- const allocator = scope.arena();
- const val_payload = switch (bit_count) {
- 16 => {
- return self.fail(scope, src, "TODO Implement addition for soft floats", .{});
- },
- 32 => blk: {
- const lhs_val = lhs.toFloat(f32);
- const rhs_val = rhs.toFloat(f32);
- const val_payload = try allocator.create(Value.Payload.Float_32);
- val_payload.* = .{ .val = lhs_val + rhs_val };
- break :blk &val_payload.base;
- },
- 64 => blk: {
- const lhs_val = lhs.toFloat(f64);
- const rhs_val = rhs.toFloat(f64);
- const val_payload = try allocator.create(Value.Payload.Float_64);
- val_payload.* = .{ .val = lhs_val + rhs_val };
- break :blk &val_payload.base;
- },
- 128 => {
- return self.fail(scope, src, "TODO Implement addition for big floats", .{});
- },
- else => unreachable,
- };
-
- return Value.initPayload(val_payload);
-}
-
-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()),
- };
-
- const allocator = scope.arena();
- const val_payload = switch (bit_count) {
- 16 => {
- return self.fail(scope, src, "TODO Implement substraction for soft floats", .{});
- },
- 32 => blk: {
- const lhs_val = lhs.toFloat(f32);
- const rhs_val = rhs.toFloat(f32);
- const val_payload = try allocator.create(Value.Payload.Float_32);
- val_payload.* = .{ .val = lhs_val - rhs_val };
- break :blk &val_payload.base;
- },
- 64 => blk: {
- const lhs_val = lhs.toFloat(f64);
- const rhs_val = rhs.toFloat(f64);
- const val_payload = try allocator.create(Value.Payload.Float_64);
- val_payload.* = .{ .val = lhs_val - rhs_val };
- break :blk &val_payload.base;
- },
- 128 => {
- return self.fail(scope, src, "TODO Implement substraction for big floats", .{});
- },
- else => unreachable,
- };
-
- return Value.initPayload(val_payload);
-}
-
-pub fn simplePtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type, mutable: bool, size: std.builtin.TypeInfo.Pointer.Size) Allocator.Error!Type {
- if (!mutable and size == .Slice and elem_ty.eql(Type.initTag(.u8))) {
- return Type.initTag(.const_slice_u8);
- }
- // TODO stage1 type inference bug
- const T = Type.Tag;
-
- const type_payload = try scope.arena().create(Type.Payload.PointerSimple);
- type_payload.* = .{
- .base = .{
- .tag = switch (size) {
- .One => if (mutable) T.single_mut_pointer else T.single_const_pointer,
- .Many => if (mutable) T.many_mut_pointer else T.many_const_pointer,
- .C => if (mutable) T.c_mut_pointer else T.c_const_pointer,
- .Slice => if (mutable) T.mut_slice else T.const_slice,
- },
- },
- .pointee_type = elem_ty,
- };
- return Type.initPayload(&type_payload.base);
-}
-
-pub fn ptrType(
- self: *Module,
- scope: *Scope,
- src: usize,
- elem_ty: Type,
- sentinel: ?Value,
- @"align": u32,
- bit_offset: u16,
- host_size: u16,
- mutable: bool,
- @"allowzero": bool,
- @"volatile": bool,
- size: std.builtin.TypeInfo.Pointer.Size,
-) Allocator.Error!Type {
- assert(host_size == 0 or bit_offset < host_size * 8);
-
- // TODO check if type can be represented by simplePtrType
- const type_payload = try scope.arena().create(Type.Payload.Pointer);
- type_payload.* = .{
- .pointee_type = elem_ty,
- .sentinel = sentinel,
- .@"align" = @"align",
- .bit_offset = bit_offset,
- .host_size = host_size,
- .@"allowzero" = @"allowzero",
- .mutable = mutable,
- .@"volatile" = @"volatile",
- .size = size,
- };
- return Type.initPayload(&type_payload.base);
-}
-
-pub fn optionalType(self: *Module, scope: *Scope, child_type: Type) Allocator.Error!Type {
- return Type.initPayload(switch (child_type.tag()) {
- .single_const_pointer => blk: {
- const payload = try scope.arena().create(Type.Payload.PointerSimple);
- payload.* = .{
- .base = .{ .tag = .optional_single_const_pointer },
- .pointee_type = child_type.elemType(),
- };
- break :blk &payload.base;
- },
- .single_mut_pointer => blk: {
- const payload = try scope.arena().create(Type.Payload.PointerSimple);
- payload.* = .{
- .base = .{ .tag = .optional_single_mut_pointer },
- .pointee_type = child_type.elemType(),
- };
- break :blk &payload.base;
- },
- else => blk: {
- const payload = try scope.arena().create(Type.Payload.Optional);
- payload.* = .{
- .child_type = child_type,
- };
- break :blk &payload.base;
- },
- });
-}
-
-pub fn arrayType(self: *Module, scope: *Scope, len: u64, sentinel: ?Value, elem_type: Type) Allocator.Error!Type {
- if (elem_type.eql(Type.initTag(.u8))) {
- if (sentinel) |some| {
- if (some.eql(Value.initTag(.zero))) {
- const payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0);
- payload.* = .{
- .len = len,
- };
- return Type.initPayload(&payload.base);
- }
- } else {
- const payload = try scope.arena().create(Type.Payload.Array_u8);
- payload.* = .{
- .len = len,
- };
- return Type.initPayload(&payload.base);
- }
- }
-
- if (sentinel) |some| {
- const payload = try scope.arena().create(Type.Payload.ArraySentinel);
- payload.* = .{
- .len = len,
- .sentinel = some,
- .elem_type = elem_type,
- };
- return Type.initPayload(&payload.base);
- }
-
- const payload = try scope.arena().create(Type.Payload.Array);
- payload.* = .{
- .len = len,
- .elem_type = elem_type,
- };
- return Type.initPayload(&payload.base);
-}
-
-pub fn errorUnionType(self: *Module, scope: *Scope, error_set: Type, payload: Type) Allocator.Error!Type {
- assert(error_set.zigTypeTag() == .ErrorSet);
- if (error_set.eql(Type.initTag(.anyerror)) and payload.eql(Type.initTag(.void))) {
- return Type.initTag(.anyerror_void_error_union);
- }
-
- const result = try scope.arena().create(Type.Payload.ErrorUnion);
- result.* = .{
- .error_set = error_set,
- .payload = payload,
- };
- return Type.initPayload(&result.base);
-}
-
-pub fn anyframeType(self: *Module, scope: *Scope, return_type: Type) Allocator.Error!Type {
- const result = try scope.arena().create(Type.Payload.AnyFrame);
- result.* = .{
- .return_type = return_type,
- };
- return Type.initPayload(&result.base);
-}
-
-pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
- const zir_module = scope.namespace();
- const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source");
- const loc = std.zig.findLineColumn(source, inst.src);
- if (inst.tag == .constant) {
- std.debug.print("constant ty={} val={} src={}:{}:{}\n", .{
- inst.ty,
- inst.castTag(.constant).?.val,
- zir_module.subFilePath(),
- loc.line + 1,
- loc.column + 1,
- });
- } else if (inst.deaths == 0) {
- std.debug.print("{} ty={} src={}:{}:{}\n", .{
- @tagName(inst.tag),
- inst.ty,
- zir_module.subFilePath(),
- loc.line + 1,
- loc.column + 1,
- });
- } else {
- std.debug.print("{} ty={} deaths={b} src={}:{}:{}\n", .{
- @tagName(inst.tag),
- inst.ty,
- inst.deaths,
- zir_module.subFilePath(),
- loc.line + 1,
- loc.column + 1,
- });
- }
-}
-
-pub const PanicId = enum {
- unreach,
- unwrap_null,
-};
-
-pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void {
- const block_inst = try parent_block.arena.create(Inst.Block);
- block_inst.* = .{
- .base = .{
- .tag = Inst.Block.base_tag,
- .ty = Type.initTag(.void),
- .src = ok.src,
- },
- .body = .{
- .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the condbr.
- },
- };
-
- const ok_body: ir.Body = .{
- .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the brvoid.
- };
- const brvoid = try parent_block.arena.create(Inst.BrVoid);
- brvoid.* = .{
- .base = .{
- .tag = .brvoid,
- .ty = Type.initTag(.noreturn),
- .src = ok.src,
- },
- .block = block_inst,
- };
- ok_body.instructions[0] = &brvoid.base;
-
- var fail_block: Scope.Block = .{
- .parent = parent_block,
- .func = parent_block.func,
- .decl = parent_block.decl,
- .instructions = .{},
- .arena = parent_block.arena,
- .is_comptime = parent_block.is_comptime,
- };
- defer fail_block.instructions.deinit(mod.gpa);
-
- _ = try mod.safetyPanic(&fail_block, ok.src, panic_id);
-
- const fail_body: ir.Body = .{ .instructions = try parent_block.arena.dupe(*Inst, fail_block.instructions.items) };
-
- const condbr = try parent_block.arena.create(Inst.CondBr);
- condbr.* = .{
- .base = .{
- .tag = .condbr,
- .ty = Type.initTag(.noreturn),
- .src = ok.src,
- },
- .condition = ok,
- .then_body = ok_body,
- .else_body = fail_body,
- };
- block_inst.body.instructions[0] = &condbr.base;
-
- try parent_block.instructions.append(mod.gpa, &block_inst.base);
-}
-
-pub fn safetyPanic(mod: *Module, block: *Scope.Block, src: usize, panic_id: PanicId) !*Inst {
- // TODO Once we have a panic function to call, call it here instead of breakpoint.
- _ = try mod.addNoOp(block, src, Type.initTag(.void), .breakpoint);
- return mod.addNoOp(block, src, Type.initTag(.noreturn), .unreach);
-}
diff --git a/src-self-hosted/Package.zig b/src-self-hosted/Package.zig
deleted file mode 100644
index 4bf4defbc8437e5358a3aef5133dd1cca1b199df..0000000000000000000000000000000000000000
--- 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/TypedValue.zig b/src-self-hosted/TypedValue.zig
deleted file mode 100644
index 48b2c04970d15593a420f73d40967f003cbec9d9..0000000000000000000000000000000000000000
--- a/src-self-hosted/TypedValue.zig
+++ /dev/null
@@ -1,31 +0,0 @@
-const std = @import("std");
-const Type = @import("type.zig").Type;
-const Value = @import("value.zig").Value;
-const Allocator = std.mem.Allocator;
-const TypedValue = @This();
-
-ty: Type,
-val: Value,
-
-/// Memory management for TypedValue. The main purpose of this type
-/// is to be small and have a deinit() function to free associated resources.
-pub const Managed = struct {
- /// If the tag value is less than Tag.no_payload_count, then no pointer
- /// dereference is needed.
- typed_value: TypedValue,
- /// If this is `null` then there is no memory management needed.
- arena: ?*std.heap.ArenaAllocator.State = null,
-
- pub fn deinit(self: *Managed, allocator: *Allocator) void {
- if (self.arena) |a| a.promote(allocator).deinit();
- self.* = undefined;
- }
-};
-
-/// Assumes arena allocation. Does a recursive copy.
-pub fn copy(self: TypedValue, allocator: *Allocator) error{OutOfMemory}!TypedValue {
- return TypedValue{
- .ty = try self.ty.copy(allocator),
- .val = try self.val.copy(allocator),
- };
-}
diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig
deleted file mode 100644
index 2c091a86eccd3cc157cb6fcbb8c2dce3e7473fd0..0000000000000000000000000000000000000000
--- a/src-self-hosted/astgen.zig
+++ /dev/null
@@ -1,2396 +0,0 @@
-const std = @import("std");
-const mem = std.mem;
-const Allocator = std.mem.Allocator;
-const Value = @import("value.zig").Value;
-const Type = @import("type.zig").Type;
-const TypedValue = @import("TypedValue.zig");
-const assert = std.debug.assert;
-const zir = @import("zir.zig");
-const Module = @import("Module.zig");
-const ast = std.zig.ast;
-const trace = @import("tracy.zig").trace;
-const Scope = Module.Scope;
-const InnerError = Module.InnerError;
-
-pub const ResultLoc = union(enum) {
- /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the
- /// expression should be generated.
- discard,
- /// The expression has an inferred type, and it will be evaluated as an rvalue.
- none,
- /// The expression must generate a pointer rather than a value. For example, the left hand side
- /// of an assignment uses this kind of result location.
- ref,
- /// The expression will be type coerced into this type, but it will be evaluated as an rvalue.
- ty: *zir.Inst,
- /// The expression must store its result into this typed pointer.
- ptr: *zir.Inst,
- /// The expression must store its result into this allocation, which has an inferred type.
- inferred_ptr: *zir.Inst.Tag.alloc_inferred.Type(),
- /// The expression must store its result into this pointer, which is a typed pointer that
- /// has been bitcasted to whatever the expression's type is.
- bitcasted_ptr: *zir.Inst.UnOp,
- /// There is a pointer for the expression to store its result into, however, its type
- /// is inferred based on peer type resolution for a `zir.Inst.Block`.
- block_ptr: *zir.Inst.Block,
-};
-
-pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst {
- const type_src = scope.tree().token_locs[type_node.firstToken()].start;
- const type_type = try addZIRInstConst(mod, scope, type_src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.type_type),
- });
- const type_rl: ResultLoc = .{ .ty = type_type };
- return expr(mod, scope, type_rl, type_node);
-}
-
-fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
- switch (node.tag) {
- .Root => unreachable,
- .Use => unreachable,
- .TestDecl => unreachable,
- .DocComment => unreachable,
- .VarDecl => unreachable,
- .SwitchCase => unreachable,
- .SwitchElse => unreachable,
- .Else => unreachable,
- .Payload => unreachable,
- .PointerPayload => unreachable,
- .PointerIndexPayload => unreachable,
- .ErrorTag => unreachable,
- .FieldInitializer => unreachable,
- .ContainerField => unreachable,
-
- .Assign,
- .AssignBitAnd,
- .AssignBitOr,
- .AssignBitShiftLeft,
- .AssignBitShiftRight,
- .AssignBitXor,
- .AssignDiv,
- .AssignSub,
- .AssignSubWrap,
- .AssignMod,
- .AssignAdd,
- .AssignAddWrap,
- .AssignMul,
- .AssignMulWrap,
- .Add,
- .AddWrap,
- .Sub,
- .SubWrap,
- .Mul,
- .MulWrap,
- .Div,
- .Mod,
- .BitAnd,
- .BitOr,
- .BitShiftLeft,
- .BitShiftRight,
- .BitXor,
- .BangEqual,
- .EqualEqual,
- .GreaterThan,
- .GreaterOrEqual,
- .LessThan,
- .LessOrEqual,
- .ArrayCat,
- .ArrayMult,
- .BoolAnd,
- .BoolOr,
- .Asm,
- .StringLiteral,
- .IntegerLiteral,
- .Call,
- .Unreachable,
- .Return,
- .If,
- .While,
- .BoolNot,
- .AddressOf,
- .FloatLiteral,
- .UndefinedLiteral,
- .BoolLiteral,
- .NullLiteral,
- .OptionalType,
- .Block,
- .LabeledBlock,
- .Break,
- .PtrType,
- .GroupedExpression,
- .ArrayType,
- .ArrayTypeSentinel,
- .EnumLiteral,
- .MultilineStringLiteral,
- .CharLiteral,
- .Defer,
- .Catch,
- .ErrorUnion,
- .MergeErrorSets,
- .Range,
- .OrElse,
- .Await,
- .BitNot,
- .Negation,
- .NegationWrap,
- .Resume,
- .Try,
- .SliceType,
- .Slice,
- .ArrayInitializer,
- .ArrayInitializerDot,
- .StructInitializer,
- .StructInitializerDot,
- .Switch,
- .For,
- .Suspend,
- .Continue,
- .AnyType,
- .ErrorType,
- .FnProto,
- .AnyFrameType,
- .ErrorSetDecl,
- .ContainerDecl,
- .Comptime,
- .Nosuspend,
- => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}),
-
- // @field can be assigned to
- .BuiltinCall => {
- const call = node.castTag(.BuiltinCall).?;
- const tree = scope.tree();
- const builtin_name = tree.tokenSlice(call.builtin_token);
-
- if (!mem.eql(u8, builtin_name, "@field")) {
- return mod.failNode(scope, node, "invalid left-hand side to assignment", .{});
- }
- },
-
- // can be assigned to
- .UnwrapOptional, .Deref, .Period, .ArrayAccess, .Identifier => {},
- }
- return expr(mod, scope, .ref, node);
-}
-
-/// Turn Zig AST into untyped ZIR istructions.
-pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {
- switch (node.tag) {
- .Root => unreachable, // Top-level declaration.
- .Use => unreachable, // Top-level declaration.
- .TestDecl => unreachable, // Top-level declaration.
- .DocComment => unreachable, // Top-level declaration.
- .VarDecl => unreachable, // Handled in `blockExpr`.
- .SwitchCase => unreachable, // Handled in `switchExpr`.
- .SwitchElse => unreachable, // Handled in `switchExpr`.
- .Else => unreachable, // Handled explicitly the control flow expression functions.
- .Payload => unreachable, // Handled explicitly.
- .PointerPayload => unreachable, // Handled explicitly.
- .PointerIndexPayload => unreachable, // Handled explicitly.
- .ErrorTag => unreachable, // Handled explicitly.
- .FieldInitializer => unreachable, // Handled explicitly.
- .ContainerField => unreachable, // Handled explicitly.
-
- .Assign => return rlWrapVoid(mod, scope, rl, node, try assign(mod, scope, node.castTag(.Assign).?)),
- .AssignBitAnd => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitAnd).?, .bitand)),
- .AssignBitOr => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitOr).?, .bitor)),
- .AssignBitShiftLeft => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftLeft).?, .shl)),
- .AssignBitShiftRight => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftRight).?, .shr)),
- .AssignBitXor => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitXor).?, .xor)),
- .AssignDiv => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignDiv).?, .div)),
- .AssignSub => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSub).?, .sub)),
- .AssignSubWrap => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSubWrap).?, .subwrap)),
- .AssignMod => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMod).?, .mod_rem)),
- .AssignAdd => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAdd).?, .add)),
- .AssignAddWrap => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAddWrap).?, .addwrap)),
- .AssignMul => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMul).?, .mul)),
- .AssignMulWrap => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMulWrap).?, .mulwrap)),
-
- .Add => return simpleBinOp(mod, scope, rl, node.castTag(.Add).?, .add),
- .AddWrap => return simpleBinOp(mod, scope, rl, node.castTag(.AddWrap).?, .addwrap),
- .Sub => return simpleBinOp(mod, scope, rl, node.castTag(.Sub).?, .sub),
- .SubWrap => return simpleBinOp(mod, scope, rl, node.castTag(.SubWrap).?, .subwrap),
- .Mul => return simpleBinOp(mod, scope, rl, node.castTag(.Mul).?, .mul),
- .MulWrap => return simpleBinOp(mod, scope, rl, node.castTag(.MulWrap).?, .mulwrap),
- .Div => return simpleBinOp(mod, scope, rl, node.castTag(.Div).?, .div),
- .Mod => return simpleBinOp(mod, scope, rl, node.castTag(.Mod).?, .mod_rem),
- .BitAnd => return simpleBinOp(mod, scope, rl, node.castTag(.BitAnd).?, .bitand),
- .BitOr => return simpleBinOp(mod, scope, rl, node.castTag(.BitOr).?, .bitor),
- .BitShiftLeft => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftLeft).?, .shl),
- .BitShiftRight => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftRight).?, .shr),
- .BitXor => return simpleBinOp(mod, scope, rl, node.castTag(.BitXor).?, .xor),
-
- .BangEqual => return simpleBinOp(mod, scope, rl, node.castTag(.BangEqual).?, .cmp_neq),
- .EqualEqual => return simpleBinOp(mod, scope, rl, node.castTag(.EqualEqual).?, .cmp_eq),
- .GreaterThan => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterThan).?, .cmp_gt),
- .GreaterOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterOrEqual).?, .cmp_gte),
- .LessThan => return simpleBinOp(mod, scope, rl, node.castTag(.LessThan).?, .cmp_lt),
- .LessOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.LessOrEqual).?, .cmp_lte),
-
- .ArrayCat => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayCat).?, .array_cat),
- .ArrayMult => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayMult).?, .array_mul),
-
- .BoolAnd => return boolBinOp(mod, scope, rl, node.castTag(.BoolAnd).?),
- .BoolOr => return boolBinOp(mod, scope, rl, node.castTag(.BoolOr).?),
-
- .BoolNot => return rlWrap(mod, scope, rl, try boolNot(mod, scope, node.castTag(.BoolNot).?)),
- .BitNot => return rlWrap(mod, scope, rl, try bitNot(mod, scope, node.castTag(.BitNot).?)),
- .Negation => return rlWrap(mod, scope, rl, try negation(mod, scope, node.castTag(.Negation).?, .sub)),
- .NegationWrap => return rlWrap(mod, scope, rl, try negation(mod, scope, node.castTag(.NegationWrap).?, .subwrap)),
-
- .Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?),
- .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)),
- .StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)),
- .IntegerLiteral => return rlWrap(mod, scope, rl, try integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?)),
- .BuiltinCall => return builtinCall(mod, scope, rl, node.castTag(.BuiltinCall).?),
- .Call => return callExpr(mod, scope, rl, node.castTag(.Call).?),
- .Unreachable => return unreach(mod, scope, node.castTag(.Unreachable).?),
- .Return => return ret(mod, scope, node.castTag(.Return).?),
- .If => return ifExpr(mod, scope, rl, node.castTag(.If).?),
- .While => return whileExpr(mod, scope, rl, node.castTag(.While).?),
- .Period => return field(mod, scope, rl, node.castTag(.Period).?),
- .Deref => return rlWrap(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)),
- .AddressOf => return rlWrap(mod, scope, rl, try addressOf(mod, scope, node.castTag(.AddressOf).?)),
- .FloatLiteral => return rlWrap(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)),
- .UndefinedLiteral => return rlWrap(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)),
- .BoolLiteral => return rlWrap(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)),
- .NullLiteral => return rlWrap(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)),
- .OptionalType => return rlWrap(mod, scope, rl, try optionalType(mod, scope, node.castTag(.OptionalType).?)),
- .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?),
- .Block => return rlWrapVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)),
- .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?, .block),
- .Break => return rlWrap(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)),
- .PtrType => return rlWrap(mod, scope, rl, try ptrType(mod, scope, node.castTag(.PtrType).?)),
- .GroupedExpression => return expr(mod, scope, rl, node.castTag(.GroupedExpression).?.expr),
- .ArrayType => return rlWrap(mod, scope, rl, try arrayType(mod, scope, node.castTag(.ArrayType).?)),
- .ArrayTypeSentinel => return rlWrap(mod, scope, rl, try arrayTypeSentinel(mod, scope, node.castTag(.ArrayTypeSentinel).?)),
- .EnumLiteral => return rlWrap(mod, scope, rl, try enumLiteral(mod, scope, node.castTag(.EnumLiteral).?)),
- .MultilineStringLiteral => return rlWrap(mod, scope, rl, try multilineStrLiteral(mod, scope, node.castTag(.MultilineStringLiteral).?)),
- .CharLiteral => return rlWrap(mod, scope, rl, try charLiteral(mod, scope, node.castTag(.CharLiteral).?)),
- .SliceType => return rlWrap(mod, scope, rl, try sliceType(mod, scope, node.castTag(.SliceType).?)),
- .ErrorUnion => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.ErrorUnion).?, .error_union_type)),
- .MergeErrorSets => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.MergeErrorSets).?, .merge_error_sets)),
- .AnyFrameType => return rlWrap(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)),
- .ErrorSetDecl => return errorSetDecl(mod, scope, rl, node.castTag(.ErrorSetDecl).?),
- .ErrorType => return rlWrap(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)),
- .For => return forExpr(mod, scope, rl, node.castTag(.For).?),
- .ArrayAccess => return arrayAccess(mod, scope, rl, node.castTag(.ArrayAccess).?),
- .Slice => return rlWrap(mod, scope, rl, try sliceExpr(mod, scope, node.castTag(.Slice).?)),
- .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?),
- .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?),
- .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?),
-
- .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),
- .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}),
- .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}),
- .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}),
- .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),
- .ArrayInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializer", .{}),
- .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}),
- .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}),
- .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}),
- .Switch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Switch", .{}),
- .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}),
- .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}),
- .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),
- .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}),
- .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}),
- .Nosuspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Nosuspend", .{}),
- }
-}
-
-fn comptimeKeyword(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Comptime) InnerError!*zir.Inst {
- const tracy = trace(@src());
- defer tracy.end();
-
- return comptimeExpr(mod, scope, rl, node.expr);
-}
-
-pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {
- const tree = parent_scope.tree();
- const src = tree.token_locs[node.firstToken()].start;
-
- // Optimization for labeled blocks: don't need to have 2 layers of blocks, we can reuse the existing one.
- if (node.castTag(.LabeledBlock)) |block_node| {
- return labeledBlockExpr(mod, parent_scope, rl, block_node, .block_comptime);
- }
-
- // Make a scope to collect generated instructions in the sub-expression.
- var block_scope: Scope.GenZIR = .{
- .parent = parent_scope,
- .decl = parent_scope.decl().?,
- .arena = parent_scope.arena(),
- .instructions = .{},
- };
- defer block_scope.instructions.deinit(mod.gpa);
-
- // No need to capture the result here because block_comptime_flat implies that the final
- // instruction is the block's result value.
- _ = try expr(mod, &block_scope.base, rl, node);
-
- const block = try addZIRInstBlock(mod, parent_scope, src, .block_comptime_flat, .{
- .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
- });
-
- return &block.base;
-}
-
-fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
- const tree = parent_scope.tree();
- const src = tree.token_locs[node.ltoken].start;
-
- if (node.getLabel()) |break_label| {
- // Look for the label in the scope.
- var scope = parent_scope;
- while (true) {
- switch (scope.tag) {
- .gen_zir => {
- const gen_zir = scope.cast(Scope.GenZIR).?;
- if (gen_zir.label) |label| {
- if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
- if (node.getRHS()) |rhs| {
- // Most result location types can be forwarded directly; however
- // if we need to write to a pointer which has an inferred type,
- // proper type inference requires peer type resolution on the block's
- // break operand expressions.
- const branch_rl: ResultLoc = switch (label.result_loc) {
- .discard, .none, .ty, .ptr, .ref => label.result_loc,
- .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = label.block_inst },
- };
- const operand = try expr(mod, parent_scope, branch_rl, rhs);
- return try addZIRInst(mod, scope, src, zir.Inst.Break, .{
- .block = label.block_inst,
- .operand = operand,
- }, .{});
- } else {
- return try addZIRInst(mod, scope, src, zir.Inst.BreakVoid, .{
- .block = label.block_inst,
- }, .{});
- }
- }
- }
- scope = gen_zir.parent;
- },
- .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
- .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
- else => {
- const label_name = try identifierTokenString(mod, parent_scope, break_label);
- return mod.failTok(parent_scope, break_label, "label not found: '{}'", .{label_name});
- },
- }
- }
- } else {
- return mod.failNode(parent_scope, &node.base, "TODO implement break from loop", .{});
- }
-}
-
-pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block) InnerError!void {
- const tracy = trace(@src());
- defer tracy.end();
-
- try blockExprStmts(mod, parent_scope, &block_node.base, block_node.statements());
-}
-
-fn labeledBlockExpr(
- mod: *Module,
- parent_scope: *Scope,
- rl: ResultLoc,
- block_node: *ast.Node.LabeledBlock,
- zir_tag: zir.Inst.Tag,
-) InnerError!*zir.Inst {
- const tracy = trace(@src());
- defer tracy.end();
-
- assert(zir_tag == .block or zir_tag == .block_comptime);
-
- const tree = parent_scope.tree();
- const src = tree.token_locs[block_node.lbrace].start;
-
- // Create the Block ZIR instruction so that we can put it into the GenZIR struct
- // so that break statements can reference it.
- const gen_zir = parent_scope.getGenZIR();
- const block_inst = try gen_zir.arena.create(zir.Inst.Block);
- block_inst.* = .{
- .base = .{
- .tag = zir_tag,
- .src = src,
- },
- .positionals = .{
- .body = .{ .instructions = undefined },
- },
- .kw_args = .{},
- };
-
- var block_scope: Scope.GenZIR = .{
- .parent = parent_scope,
- .decl = parent_scope.decl().?,
- .arena = gen_zir.arena,
- .instructions = .{},
- // TODO @as here is working around a stage1 miscompilation bug :(
- .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
- .token = block_node.label,
- .block_inst = block_inst,
- .result_loc = rl,
- }),
- };
- defer block_scope.instructions.deinit(mod.gpa);
-
- try blockExprStmts(mod, &block_scope.base, &block_node.base, block_node.statements());
-
- block_inst.positionals.body.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items);
- try gen_zir.instructions.append(mod.gpa, &block_inst.base);
-
- return &block_inst.base;
-}
-
-fn blockExprStmts(mod: *Module, parent_scope: *Scope, node: *ast.Node, statements: []*ast.Node) !void {
- const tree = parent_scope.tree();
-
- var block_arena = std.heap.ArenaAllocator.init(mod.gpa);
- defer block_arena.deinit();
-
- var scope = parent_scope;
- for (statements) |statement| {
- const src = tree.token_locs[statement.firstToken()].start;
- _ = try addZIRNoOp(mod, scope, src, .dbg_stmt);
- switch (statement.tag) {
- .VarDecl => {
- const var_decl_node = statement.castTag(.VarDecl).?;
- scope = try varDecl(mod, scope, var_decl_node, &block_arena.allocator);
- },
- .Assign => try assign(mod, scope, statement.castTag(.Assign).?),
- .AssignBitAnd => try assignOp(mod, scope, statement.castTag(.AssignBitAnd).?, .bitand),
- .AssignBitOr => try assignOp(mod, scope, statement.castTag(.AssignBitOr).?, .bitor),
- .AssignBitShiftLeft => try assignOp(mod, scope, statement.castTag(.AssignBitShiftLeft).?, .shl),
- .AssignBitShiftRight => try assignOp(mod, scope, statement.castTag(.AssignBitShiftRight).?, .shr),
- .AssignBitXor => try assignOp(mod, scope, statement.castTag(.AssignBitXor).?, .xor),
- .AssignDiv => try assignOp(mod, scope, statement.castTag(.AssignDiv).?, .div),
- .AssignSub => try assignOp(mod, scope, statement.castTag(.AssignSub).?, .sub),
- .AssignSubWrap => try assignOp(mod, scope, statement.castTag(.AssignSubWrap).?, .subwrap),
- .AssignMod => try assignOp(mod, scope, statement.castTag(.AssignMod).?, .mod_rem),
- .AssignAdd => try assignOp(mod, scope, statement.castTag(.AssignAdd).?, .add),
- .AssignAddWrap => try assignOp(mod, scope, statement.castTag(.AssignAddWrap).?, .addwrap),
- .AssignMul => try assignOp(mod, scope, statement.castTag(.AssignMul).?, .mul),
- .AssignMulWrap => try assignOp(mod, scope, statement.castTag(.AssignMulWrap).?, .mulwrap),
-
- else => {
- const possibly_unused_result = try expr(mod, scope, .none, statement);
- if (!possibly_unused_result.tag.isNoReturn()) {
- _ = try addZIRUnOp(mod, scope, src, .ensure_result_used, possibly_unused_result);
- }
- },
- }
- }
-}
-
-fn varDecl(
- mod: *Module,
- scope: *Scope,
- node: *ast.Node.VarDecl,
- block_arena: *Allocator,
-) InnerError!*Scope {
- // TODO implement detection of shadowing
- if (node.getComptimeToken()) |comptime_token| {
- return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{});
- }
- if (node.getAlignNode()) |align_node| {
- return mod.failNode(scope, align_node, "TODO implement alignment on locals", .{});
- }
- const tree = scope.tree();
- const name_src = tree.token_locs[node.name_token].start;
- const ident_name = try identifierTokenString(mod, scope, node.name_token);
- const init_node = node.getInitNode() orelse
- return mod.fail(scope, name_src, "variables must be initialized", .{});
-
- switch (tree.token_ids[node.mut_token]) {
- .Keyword_const => {
- // Depending on the type of AST the initialization expression is, we may need an lvalue
- // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
- // the variable, no memory location needed.
- const result_loc = if (nodeMayNeedMemoryLocation(init_node)) r: {
- if (node.getTypeNode()) |type_node| {
- const type_inst = try typeExpr(mod, scope, type_node);
- const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);
- break :r ResultLoc{ .ptr = alloc };
- } else {
- const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred);
- break :r ResultLoc{ .inferred_ptr = alloc };
- }
- } else r: {
- if (node.getTypeNode()) |type_node|
- break :r ResultLoc{ .ty = try typeExpr(mod, scope, type_node) }
- else
- break :r .none;
- };
- const init_inst = try expr(mod, scope, result_loc, init_node);
- const sub_scope = try block_arena.create(Scope.LocalVal);
- sub_scope.* = .{
- .parent = scope,
- .gen_zir = scope.getGenZIR(),
- .name = ident_name,
- .inst = init_inst,
- };
- return &sub_scope.base;
- },
- .Keyword_var => {
- const var_data: struct { result_loc: ResultLoc, alloc: *zir.Inst } = if (node.getTypeNode()) |type_node| a: {
- const type_inst = try typeExpr(mod, scope, type_node);
- const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);
- break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
- } else a: {
- const alloc = try addZIRNoOp(mod, scope, name_src, .alloc_inferred);
- break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred).? } };
- };
- const init_inst = try expr(mod, scope, var_data.result_loc, init_node);
- const sub_scope = try block_arena.create(Scope.LocalPtr);
- sub_scope.* = .{
- .parent = scope,
- .gen_zir = scope.getGenZIR(),
- .name = ident_name,
- .ptr = var_data.alloc,
- };
- return &sub_scope.base;
- },
- else => unreachable,
- }
-}
-
-fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!void {
- if (infix_node.lhs.castTag(.Identifier)) |ident| {
- // This intentionally does not support @"_" syntax.
- const ident_name = scope.tree().tokenSlice(ident.token);
- if (mem.eql(u8, ident_name, "_")) {
- _ = try expr(mod, scope, .discard, infix_node.rhs);
- return;
- }
- }
- const lvalue = try lvalExpr(mod, scope, infix_node.lhs);
- _ = try expr(mod, scope, .{ .ptr = lvalue }, infix_node.rhs);
-}
-
-fn assignOp(
- mod: *Module,
- scope: *Scope,
- infix_node: *ast.Node.SimpleInfixOp,
- op_inst_tag: zir.Inst.Tag,
-) InnerError!void {
- const lhs_ptr = try lvalExpr(mod, scope, infix_node.lhs);
- const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr);
- const lhs_type = try addZIRUnOp(mod, scope, lhs_ptr.src, .typeof, lhs);
- const rhs = try expr(mod, scope, .{ .ty = lhs_type }, infix_node.rhs);
-
- const tree = scope.tree();
- const src = tree.token_locs[infix_node.op_token].start;
-
- const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
- _ = try addZIRBinOp(mod, scope, src, .store, lhs_ptr, result);
-}
-
-fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.op_token].start;
- const bool_type = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.bool_type),
- });
- const operand = try expr(mod, scope, .{ .ty = bool_type }, node.rhs);
- return addZIRUnOp(mod, scope, src, .boolnot, operand);
-}
-
-fn bitNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.op_token].start;
- const operand = try expr(mod, scope, .none, node.rhs);
- return addZIRUnOp(mod, scope, src, .bitnot, operand);
-}
-
-fn negation(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp, op_inst_tag: zir.Inst.Tag) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.op_token].start;
-
- const lhs = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.comptime_int),
- .val = Value.initTag(.zero),
- });
- const rhs = try expr(mod, scope, .none, node.rhs);
-
- return addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
-}
-
-fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
- return expr(mod, scope, .ref, node.rhs);
-}
-
-fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.op_token].start;
- const operand = try typeExpr(mod, scope, node.rhs);
- return addZIRUnOp(mod, scope, src, .optional_type, operand);
-}
-
-fn sliceType(mod: *Module, scope: *Scope, node: *ast.Node.SliceType) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.op_token].start;
- return ptrSliceType(mod, scope, src, &node.ptr_info, node.rhs, .Slice);
-}
-
-fn ptrType(mod: *Module, scope: *Scope, node: *ast.Node.PtrType) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.op_token].start;
- return ptrSliceType(mod, scope, src, &node.ptr_info, node.rhs, switch (tree.token_ids[node.op_token]) {
- .Asterisk, .AsteriskAsterisk => .One,
- // TODO stage1 type inference bug
- .LBracket => @as(std.builtin.TypeInfo.Pointer.Size, switch (tree.token_ids[node.op_token + 2]) {
- .Identifier => .C,
- else => .Many,
- }),
- else => unreachable,
- });
-}
-
-fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, rhs: *ast.Node, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*zir.Inst {
- const simple = ptr_info.allowzero_token == null and
- ptr_info.align_info == null and
- ptr_info.volatile_token == null and
- ptr_info.sentinel == null;
-
- if (simple) {
- const child_type = try typeExpr(mod, scope, rhs);
- const mutable = ptr_info.const_token == null;
- // TODO stage1 type inference bug
- const T = zir.Inst.Tag;
- return addZIRUnOp(mod, scope, src, switch (size) {
- .One => if (mutable) T.single_mut_ptr_type else T.single_const_ptr_type,
- .Many => if (mutable) T.many_mut_ptr_type else T.many_const_ptr_type,
- .C => if (mutable) T.c_mut_ptr_type else T.c_const_ptr_type,
- .Slice => if (mutable) T.mut_slice_type else T.const_slice_type,
- }, child_type);
- }
-
- var kw_args: std.meta.fieldInfo(zir.Inst.PtrType, "kw_args").field_type = .{};
- kw_args.size = size;
- kw_args.@"allowzero" = ptr_info.allowzero_token != null;
- if (ptr_info.align_info) |some| {
- kw_args.@"align" = try expr(mod, scope, .none, some.node);
- if (some.bit_range) |bit_range| {
- kw_args.align_bit_start = try expr(mod, scope, .none, bit_range.start);
- kw_args.align_bit_end = try expr(mod, scope, .none, bit_range.end);
- }
- }
- kw_args.mutable = ptr_info.const_token == null;
- kw_args.@"volatile" = ptr_info.volatile_token != null;
- if (ptr_info.sentinel) |some| {
- kw_args.sentinel = try expr(mod, scope, .none, some);
- }
-
- const child_type = try typeExpr(mod, scope, rhs);
- if (kw_args.sentinel) |some| {
- kw_args.sentinel = try addZIRBinOp(mod, scope, some.src, .as, child_type, some);
- }
-
- return addZIRInst(mod, scope, src, zir.Inst.PtrType, .{ .child_type = child_type }, kw_args);
-}
-
-fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.op_token].start;
- const usize_type = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.usize_type),
- });
-
- // TODO check for [_]T
- const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr);
- const elem_type = try typeExpr(mod, scope, node.rhs);
-
- return addZIRBinOp(mod, scope, src, .array_type, len, elem_type);
-}
-
-fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSentinel) !*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.op_token].start;
- const usize_type = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.usize_type),
- });
-
- // TODO check for [_]T
- const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr);
- const sentinel_uncasted = try expr(mod, scope, .none, node.sentinel);
- const elem_type = try typeExpr(mod, scope, node.rhs);
- const sentinel = try addZIRBinOp(mod, scope, src, .as, elem_type, sentinel_uncasted);
-
- return addZIRInst(mod, scope, src, zir.Inst.ArrayTypeSentinel, .{
- .len = len,
- .sentinel = sentinel,
- .elem_type = elem_type,
- }, .{});
-}
-
-fn anyFrameType(mod: *Module, scope: *Scope, node: *ast.Node.AnyFrameType) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.anyframe_token].start;
- if (node.result) |some| {
- const return_type = try typeExpr(mod, scope, some.return_type);
- return addZIRUnOp(mod, scope, src, .anyframe_type, return_type);
- } else {
- return addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.anyframe_type),
- });
- }
-}
-
-fn typeInixOp(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp, op_inst_tag: zir.Inst.Tag) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.op_token].start;
- const error_set = try typeExpr(mod, scope, node.lhs);
- const payload = try typeExpr(mod, scope, node.rhs);
- return addZIRBinOp(mod, scope, src, op_inst_tag, error_set, payload);
-}
-
-fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.name].start;
- const name = try identifierTokenString(mod, scope, node.name);
-
- return addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{});
-}
-
-fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.rtoken].start;
-
- const operand = try expr(mod, scope, .ref, node.lhs);
- return rlWrapPtr(mod, scope, rl, try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand));
-}
-
-fn errorSetDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ErrorSetDecl) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.error_token].start;
- const decls = node.decls();
- const fields = try scope.arena().alloc([]const u8, decls.len);
-
- for (decls) |decl, i| {
- const tag = decl.castTag(.ErrorTag).?;
- fields[i] = try identifierTokenString(mod, scope, tag.name_token);
- }
-
- // analyzing the error set results in a decl ref, so we might need to dereference it
- return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{}));
-}
-
-fn errorType(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.token].start;
- return addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.anyerror_type),
- });
-}
-
-fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Catch) InnerError!*zir.Inst {
- return orelseCatchExpr(mod, scope, rl, node.lhs, node.op_token, .iserr, .unwrap_err_unsafe, node.rhs, node.payload);
-}
-
-fn orelseExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
- return orelseCatchExpr(mod, scope, rl, node.lhs, node.op_token, .isnull, .unwrap_optional_unsafe, node.rhs, null);
-}
-
-fn orelseCatchExpr(
- mod: *Module,
- scope: *Scope,
- rl: ResultLoc,
- lhs: *ast.Node,
- op_token: ast.TokenIndex,
- cond_op: zir.Inst.Tag,
- unwrap_op: zir.Inst.Tag,
- rhs: *ast.Node,
- payload_node: ?*ast.Node,
-) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[op_token].start;
-
- const operand_ptr = try expr(mod, scope, .ref, lhs);
- // TODO we could avoid an unnecessary copy if .iserr, .isnull took a pointer
- const err_union = try addZIRUnOp(mod, scope, src, .deref, operand_ptr);
- const cond = try addZIRUnOp(mod, scope, src, cond_op, err_union);
-
- var block_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = scope.decl().?,
- .arena = scope.arena(),
- .instructions = .{},
- };
- defer block_scope.instructions.deinit(mod.gpa);
-
- const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
- .condition = cond,
- .then_body = undefined, // populated below
- .else_body = undefined, // populated below
- }, .{});
-
- const block = try addZIRInstBlock(mod, scope, src, .block, .{
- .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
- });
-
- // Most result location types can be forwarded directly; however
- // if we need to write to a pointer which has an inferred type,
- // proper type inference requires peer type resolution on the if's
- // branches.
- const branch_rl: ResultLoc = switch (rl) {
- .discard, .none, .ty, .ptr, .ref => rl,
- .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
- };
-
- var then_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = block_scope.decl,
- .arena = block_scope.arena,
- .instructions = .{},
- };
- defer then_scope.instructions.deinit(mod.gpa);
-
- var err_val_scope: Scope.LocalVal = undefined;
- const then_sub_scope = blk: {
- const payload = payload_node orelse
- break :blk &then_scope.base;
-
- const err_name = tree.tokenSlice(payload.castTag(.Payload).?.error_symbol.firstToken());
- if (mem.eql(u8, err_name, "_"))
- break :blk &then_scope.base;
-
- const unwrapped_err_ptr = try addZIRUnOp(mod, &then_scope.base, src, .unwrap_err_code, operand_ptr);
- err_val_scope = .{
- .parent = &then_scope.base,
- .gen_zir = &then_scope,
- .name = err_name,
- .inst = try addZIRUnOp(mod, &then_scope.base, src, .deref, unwrapped_err_ptr),
- };
- break :blk &err_val_scope.base;
- };
-
- _ = try addZIRInst(mod, &then_scope.base, src, zir.Inst.Break, .{
- .block = block,
- .operand = try expr(mod, then_sub_scope, branch_rl, rhs),
- }, .{});
-
- var else_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = block_scope.decl,
- .arena = block_scope.arena,
- .instructions = .{},
- };
- defer else_scope.instructions.deinit(mod.gpa);
-
- const unwrapped_payload = try addZIRUnOp(mod, &else_scope.base, src, unwrap_op, operand_ptr);
- _ = try addZIRInst(mod, &else_scope.base, src, zir.Inst.Break, .{
- .block = block,
- .operand = unwrapped_payload,
- }, .{});
-
- condbr.positionals.then_body = .{ .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items) };
- condbr.positionals.else_body = .{ .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items) };
- return rlWrapPtr(mod, scope, rl, &block.base);
-}
-
-/// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating.
-/// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used.
-fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {
- const ident_name_1 = try identifierTokenString(mod, scope, token1);
- const ident_name_2 = try identifierTokenString(mod, scope, token2);
- return mem.eql(u8, ident_name_1, ident_name_2);
-}
-
-/// Identifier token -> String (allocated in scope.arena())
-fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
- const tree = scope.tree();
-
- const ident_name = tree.tokenSlice(token);
- if (mem.startsWith(u8, ident_name, "@")) {
- const raw_string = ident_name[1..];
- var bad_index: usize = undefined;
- return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) {
- error.InvalidCharacter => {
- const bad_byte = raw_string[bad_index];
- const src = tree.token_locs[token].start;
- return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
- },
- else => |e| return e,
- };
- }
- return ident_name;
-}
-
-pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.token].start;
-
- const ident_name = try identifierTokenString(mod, scope, node.token);
-
- return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{});
-}
-
-fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.op_token].start;
-
- const lhs = try expr(mod, scope, .ref, node.lhs);
- const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?);
-
- return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{}));
-}
-
-fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ArrayAccess) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.rtoken].start;
-
- const array_ptr = try expr(mod, scope, .ref, node.lhs);
- const index = try expr(mod, scope, .none, node.index_expr);
-
- return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ElemPtr, .{ .array_ptr = array_ptr, .index = index }, .{}));
-}
-
-fn sliceExpr(mod: *Module, scope: *Scope, node: *ast.Node.Slice) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.rtoken].start;
-
- const usize_type = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.usize_type),
- });
-
- const array_ptr = try expr(mod, scope, .ref, node.lhs);
- const start = try expr(mod, scope, .{ .ty = usize_type }, node.start);
-
- if (node.end == null and node.sentinel == null) {
- return try addZIRBinOp(mod, scope, src, .slice_start, array_ptr, start);
- }
-
- const end = if (node.end) |end| try expr(mod, scope, .{ .ty = usize_type }, end) else null;
- // we could get the child type here, but it is easier to just do it in semantic analysis.
- const sentinel = if (node.sentinel) |sentinel| try expr(mod, scope, .none, sentinel) else null;
-
- return try addZIRInst(
- mod,
- scope,
- src,
- zir.Inst.Slice,
- .{ .array_ptr = array_ptr, .start = start },
- .{ .end = end, .sentinel = sentinel },
- );
-}
-
-fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.rtoken].start;
- const lhs = try expr(mod, scope, .none, node.lhs);
- return addZIRUnOp(mod, scope, src, .deref, lhs);
-}
-
-fn simpleBinOp(
- mod: *Module,
- scope: *Scope,
- rl: ResultLoc,
- infix_node: *ast.Node.SimpleInfixOp,
- op_inst_tag: zir.Inst.Tag,
-) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[infix_node.op_token].start;
-
- const lhs = try expr(mod, scope, .none, infix_node.lhs);
- const rhs = try expr(mod, scope, .none, infix_node.rhs);
-
- const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
- return rlWrap(mod, scope, rl, result);
-}
-
-fn boolBinOp(
- mod: *Module,
- scope: *Scope,
- rl: ResultLoc,
- infix_node: *ast.Node.SimpleInfixOp,
-) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[infix_node.op_token].start;
- const bool_type = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.bool_type),
- });
-
- var block_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = scope.decl().?,
- .arena = scope.arena(),
- .instructions = .{},
- };
- defer block_scope.instructions.deinit(mod.gpa);
-
- const lhs = try expr(mod, scope, .{ .ty = bool_type }, infix_node.lhs);
- const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
- .condition = lhs,
- .then_body = undefined, // populated below
- .else_body = undefined, // populated below
- }, .{});
-
- const block = try addZIRInstBlock(mod, scope, src, .block, .{
- .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
- });
-
- var rhs_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = block_scope.decl,
- .arena = block_scope.arena,
- .instructions = .{},
- };
- defer rhs_scope.instructions.deinit(mod.gpa);
-
- const rhs = try expr(mod, &rhs_scope.base, .{ .ty = bool_type }, infix_node.rhs);
- _ = try addZIRInst(mod, &rhs_scope.base, src, zir.Inst.Break, .{
- .block = block,
- .operand = rhs,
- }, .{});
-
- var const_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = block_scope.decl,
- .arena = block_scope.arena,
- .instructions = .{},
- };
- defer const_scope.instructions.deinit(mod.gpa);
-
- const is_bool_and = infix_node.base.tag == .BoolAnd;
- _ = try addZIRInst(mod, &const_scope.base, src, zir.Inst.Break, .{
- .block = block,
- .operand = try addZIRInstConst(mod, &const_scope.base, src, .{
- .ty = Type.initTag(.bool),
- .val = if (is_bool_and) Value.initTag(.bool_false) else Value.initTag(.bool_true),
- }),
- }, .{});
-
- if (is_bool_and) {
- // if lhs // AND
- // break rhs
- // else
- // break false
- condbr.positionals.then_body = .{ .instructions = try rhs_scope.arena.dupe(*zir.Inst, rhs_scope.instructions.items) };
- condbr.positionals.else_body = .{ .instructions = try const_scope.arena.dupe(*zir.Inst, const_scope.instructions.items) };
- } else {
- // if lhs // OR
- // break true
- // else
- // break rhs
- condbr.positionals.then_body = .{ .instructions = try const_scope.arena.dupe(*zir.Inst, const_scope.instructions.items) };
- condbr.positionals.else_body = .{ .instructions = try rhs_scope.arena.dupe(*zir.Inst, rhs_scope.instructions.items) };
- }
-
- return rlWrap(mod, scope, rl, &block.base);
-}
-
-const CondKind = union(enum) {
- bool,
- optional: ?*zir.Inst,
- err_union: ?*zir.Inst,
-
- fn cond(self: *CondKind, mod: *Module, block_scope: *Scope.GenZIR, src: usize, cond_node: *ast.Node) !*zir.Inst {
- switch (self.*) {
- .bool => {
- const bool_type = try addZIRInstConst(mod, &block_scope.base, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.bool_type),
- });
- return try expr(mod, &block_scope.base, .{ .ty = bool_type }, cond_node);
- },
- .optional => {
- const cond_ptr = try expr(mod, &block_scope.base, .ref, cond_node);
- self.* = .{ .optional = cond_ptr };
- const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, cond_ptr);
- return try addZIRUnOp(mod, &block_scope.base, src, .isnonnull, result);
- },
- .err_union => {
- const err_ptr = try expr(mod, &block_scope.base, .ref, cond_node);
- self.* = .{ .err_union = err_ptr };
- const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, err_ptr);
- return try addZIRUnOp(mod, &block_scope.base, src, .iserr, result);
- },
- }
- }
-
- fn thenSubScope(self: CondKind, mod: *Module, then_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope {
- if (self == .bool) return &then_scope.base;
-
- const payload = payload_node.?.castTag(.PointerPayload) orelse {
- // condition is error union and payload is not explicitly ignored
- _ = try addZIRUnOp(mod, &then_scope.base, src, .ensure_err_payload_void, self.err_union.?);
- return &then_scope.base;
- };
- const is_ptr = payload.ptr_token != null;
- const ident_node = payload.value_symbol.castTag(.Identifier).?;
-
- // This intentionally does not support @"_" syntax.
- const ident_name = then_scope.base.tree().tokenSlice(ident_node.token);
- if (mem.eql(u8, ident_name, "_")) {
- if (is_ptr)
- return mod.failTok(&then_scope.base, payload.ptr_token.?, "pointer modifier invalid on discard", .{});
- return &then_scope.base;
- }
-
- return mod.failNode(&then_scope.base, payload.value_symbol, "TODO implement payload symbols", .{});
- }
-
- fn elseSubScope(self: CondKind, mod: *Module, else_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope {
- if (self != .err_union) return &else_scope.base;
-
- const payload_ptr = try addZIRUnOp(mod, &else_scope.base, src, .unwrap_err_unsafe, self.err_union.?);
-
- const payload = payload_node.?.castTag(.Payload).?;
- const ident_node = payload.error_symbol.castTag(.Identifier).?;
-
- // This intentionally does not support @"_" syntax.
- const ident_name = else_scope.base.tree().tokenSlice(ident_node.token);
- if (mem.eql(u8, ident_name, "_")) {
- return &else_scope.base;
- }
-
- return mod.failNode(&else_scope.base, payload.error_symbol, "TODO implement payload symbols", .{});
- }
-};
-
-fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) InnerError!*zir.Inst {
- var cond_kind: CondKind = .bool;
- if (if_node.payload) |_| cond_kind = .{ .optional = null };
- if (if_node.@"else") |else_node| {
- if (else_node.payload) |payload| {
- cond_kind = .{ .err_union = null };
- }
- }
- var block_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = scope.decl().?,
- .arena = scope.arena(),
- .instructions = .{},
- };
- defer block_scope.instructions.deinit(mod.gpa);
-
- const tree = scope.tree();
- const if_src = tree.token_locs[if_node.if_token].start;
- const cond = try cond_kind.cond(mod, &block_scope, if_src, if_node.condition);
-
- const condbr = try addZIRInstSpecial(mod, &block_scope.base, if_src, zir.Inst.CondBr, .{
- .condition = cond,
- .then_body = undefined, // populated below
- .else_body = undefined, // populated below
- }, .{});
-
- const block = try addZIRInstBlock(mod, scope, if_src, .block, .{
- .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
- });
-
- const then_src = tree.token_locs[if_node.body.lastToken()].start;
- var then_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = block_scope.decl,
- .arena = block_scope.arena,
- .instructions = .{},
- };
- defer then_scope.instructions.deinit(mod.gpa);
-
- // declare payload to the then_scope
- const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, if_node.payload);
-
- // Most result location types can be forwarded directly; however
- // if we need to write to a pointer which has an inferred type,
- // proper type inference requires peer type resolution on the if's
- // branches.
- const branch_rl: ResultLoc = switch (rl) {
- .discard, .none, .ty, .ptr, .ref => rl,
- .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
- };
-
- const then_result = try expr(mod, then_sub_scope, branch_rl, if_node.body);
- if (!then_result.tag.isNoReturn()) {
- _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{
- .block = block,
- .operand = then_result,
- }, .{});
- }
- condbr.positionals.then_body = .{
- .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
- };
-
- var else_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = block_scope.decl,
- .arena = block_scope.arena,
- .instructions = .{},
- };
- defer else_scope.instructions.deinit(mod.gpa);
-
- if (if_node.@"else") |else_node| {
- const else_src = tree.token_locs[else_node.body.lastToken()].start;
- // declare payload to the then_scope
- const else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload);
-
- const else_result = try expr(mod, else_sub_scope, branch_rl, else_node.body);
- if (!else_result.tag.isNoReturn()) {
- _ = try addZIRInst(mod, else_sub_scope, else_src, zir.Inst.Break, .{
- .block = block,
- .operand = else_result,
- }, .{});
- }
- } else {
- // TODO Optimization opportunity: we can avoid an allocation and a memcpy here
- // by directly allocating the body for this one instruction.
- const else_src = tree.token_locs[if_node.lastToken()].start;
- _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.BreakVoid, .{
- .block = block,
- }, .{});
- }
- condbr.positionals.else_body = .{
- .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
- };
-
- return &block.base;
-}
-
-fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.While) InnerError!*zir.Inst {
- var cond_kind: CondKind = .bool;
- if (while_node.payload) |_| cond_kind = .{ .optional = null };
- if (while_node.@"else") |else_node| {
- if (else_node.payload) |payload| {
- cond_kind = .{ .err_union = null };
- }
- }
-
- if (while_node.label) |tok|
- return mod.failTok(scope, tok, "TODO labeled while", .{});
-
- if (while_node.inline_token) |tok|
- return mod.failTok(scope, tok, "TODO inline while", .{});
-
- var expr_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = scope.decl().?,
- .arena = scope.arena(),
- .instructions = .{},
- };
- defer expr_scope.instructions.deinit(mod.gpa);
-
- var loop_scope: Scope.GenZIR = .{
- .parent = &expr_scope.base,
- .decl = expr_scope.decl,
- .arena = expr_scope.arena,
- .instructions = .{},
- };
- defer loop_scope.instructions.deinit(mod.gpa);
-
- var continue_scope: Scope.GenZIR = .{
- .parent = &loop_scope.base,
- .decl = loop_scope.decl,
- .arena = loop_scope.arena,
- .instructions = .{},
- };
- defer continue_scope.instructions.deinit(mod.gpa);
-
- const tree = scope.tree();
- const while_src = tree.token_locs[while_node.while_token].start;
- const void_type = try addZIRInstConst(mod, scope, while_src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.void_type),
- });
- const cond = try cond_kind.cond(mod, &continue_scope, while_src, while_node.condition);
-
- const condbr = try addZIRInstSpecial(mod, &continue_scope.base, while_src, zir.Inst.CondBr, .{
- .condition = cond,
- .then_body = undefined, // populated below
- .else_body = undefined, // populated below
- }, .{});
- const cond_block = try addZIRInstBlock(mod, &loop_scope.base, while_src, .block, .{
- .instructions = try loop_scope.arena.dupe(*zir.Inst, continue_scope.instructions.items),
- });
- // TODO avoid emitting the continue expr when there
- // are no jumps to it. This happens when the last statement of a while body is noreturn
- // and there are no `continue` statements.
- // The "repeat" at the end of a loop body is implied.
- if (while_node.continue_expr) |cont_expr| {
- _ = try expr(mod, &loop_scope.base, .{ .ty = void_type }, cont_expr);
- }
- const loop = try addZIRInstLoop(mod, &expr_scope.base, while_src, .{
- .instructions = try expr_scope.arena.dupe(*zir.Inst, loop_scope.instructions.items),
- });
- const while_block = try addZIRInstBlock(mod, scope, while_src, .block, .{
- .instructions = try expr_scope.arena.dupe(*zir.Inst, expr_scope.instructions.items),
- });
-
- const then_src = tree.token_locs[while_node.body.lastToken()].start;
- var then_scope: Scope.GenZIR = .{
- .parent = &continue_scope.base,
- .decl = continue_scope.decl,
- .arena = continue_scope.arena,
- .instructions = .{},
- };
- defer then_scope.instructions.deinit(mod.gpa);
-
- // declare payload to the then_scope
- const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, while_node.payload);
-
- // Most result location types can be forwarded directly; however
- // if we need to write to a pointer which has an inferred type,
- // proper type inference requires peer type resolution on the while's
- // branches.
- const branch_rl: ResultLoc = switch (rl) {
- .discard, .none, .ty, .ptr, .ref => rl,
- .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = while_block },
- };
-
- const then_result = try expr(mod, then_sub_scope, branch_rl, while_node.body);
- if (!then_result.tag.isNoReturn()) {
- _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{
- .block = cond_block,
- .operand = then_result,
- }, .{});
- }
- condbr.positionals.then_body = .{
- .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
- };
-
- var else_scope: Scope.GenZIR = .{
- .parent = &continue_scope.base,
- .decl = continue_scope.decl,
- .arena = continue_scope.arena,
- .instructions = .{},
- };
- defer else_scope.instructions.deinit(mod.gpa);
-
- if (while_node.@"else") |else_node| {
- const else_src = tree.token_locs[else_node.body.lastToken()].start;
- // declare payload to the then_scope
- const else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload);
-
- const else_result = try expr(mod, else_sub_scope, branch_rl, else_node.body);
- if (!else_result.tag.isNoReturn()) {
- _ = try addZIRInst(mod, else_sub_scope, else_src, zir.Inst.Break, .{
- .block = while_block,
- .operand = else_result,
- }, .{});
- }
- } else {
- const else_src = tree.token_locs[while_node.lastToken()].start;
- _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.BreakVoid, .{
- .block = while_block,
- }, .{});
- }
- condbr.positionals.else_body = .{
- .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
- };
- return &while_block.base;
-}
-
-fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For) InnerError!*zir.Inst {
- if (for_node.label) |tok|
- return mod.failTok(scope, tok, "TODO labeled for", .{});
-
- if (for_node.inline_token) |tok|
- return mod.failTok(scope, tok, "TODO inline for", .{});
-
- var for_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = scope.decl().?,
- .arena = scope.arena(),
- .instructions = .{},
- };
- defer for_scope.instructions.deinit(mod.gpa);
-
- // setup variables and constants
- const tree = scope.tree();
- const for_src = tree.token_locs[for_node.for_token].start;
- const index_ptr = blk: {
- const usize_type = try addZIRInstConst(mod, &for_scope.base, for_src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.usize_type),
- });
- const index_ptr = try addZIRUnOp(mod, &for_scope.base, for_src, .alloc, usize_type);
- // initialize to zero
- const zero = try addZIRInstConst(mod, &for_scope.base, for_src, .{
- .ty = Type.initTag(.usize),
- .val = Value.initTag(.zero),
- });
- _ = try addZIRBinOp(mod, &for_scope.base, for_src, .store, index_ptr, zero);
- break :blk index_ptr;
- };
- const array_ptr = try expr(mod, &for_scope.base, .ref, for_node.array_expr);
- _ = try addZIRUnOp(mod, &for_scope.base, for_node.array_expr.firstToken(), .ensure_indexable, array_ptr);
- const cond_src = tree.token_locs[for_node.array_expr.firstToken()].start;
- const len_ptr = try addZIRInst(mod, &for_scope.base, cond_src, zir.Inst.FieldPtr, .{
- .object_ptr = array_ptr,
- .field_name = try addZIRInst(mod, &for_scope.base, cond_src, zir.Inst.Str, .{ .bytes = "len" }, .{}),
- }, .{});
-
- var loop_scope: Scope.GenZIR = .{
- .parent = &for_scope.base,
- .decl = for_scope.decl,
- .arena = for_scope.arena,
- .instructions = .{},
- };
- defer loop_scope.instructions.deinit(mod.gpa);
-
- var cond_scope: Scope.GenZIR = .{
- .parent = &loop_scope.base,
- .decl = loop_scope.decl,
- .arena = loop_scope.arena,
- .instructions = .{},
- };
- defer cond_scope.instructions.deinit(mod.gpa);
-
- // check condition i < array_expr.len
- const index = try addZIRUnOp(mod, &cond_scope.base, cond_src, .deref, index_ptr);
- const len = try addZIRUnOp(mod, &cond_scope.base, cond_src, .deref, len_ptr);
- const cond = try addZIRBinOp(mod, &cond_scope.base, cond_src, .cmp_lt, index, len);
-
- const condbr = try addZIRInstSpecial(mod, &cond_scope.base, for_src, zir.Inst.CondBr, .{
- .condition = cond,
- .then_body = undefined, // populated below
- .else_body = undefined, // populated below
- }, .{});
- const cond_block = try addZIRInstBlock(mod, &loop_scope.base, for_src, .block, .{
- .instructions = try loop_scope.arena.dupe(*zir.Inst, cond_scope.instructions.items),
- });
-
- // increment index variable
- const one = try addZIRInstConst(mod, &loop_scope.base, for_src, .{
- .ty = Type.initTag(.usize),
- .val = Value.initTag(.one),
- });
- const index_2 = try addZIRUnOp(mod, &loop_scope.base, cond_src, .deref, index_ptr);
- const index_plus_one = try addZIRBinOp(mod, &loop_scope.base, for_src, .add, index_2, one);
- _ = try addZIRBinOp(mod, &loop_scope.base, for_src, .store, index_ptr, index_plus_one);
-
- // looping stuff
- const loop = try addZIRInstLoop(mod, &for_scope.base, for_src, .{
- .instructions = try for_scope.arena.dupe(*zir.Inst, loop_scope.instructions.items),
- });
- const for_block = try addZIRInstBlock(mod, scope, for_src, .block, .{
- .instructions = try for_scope.arena.dupe(*zir.Inst, for_scope.instructions.items),
- });
-
- // while body
- const then_src = tree.token_locs[for_node.body.lastToken()].start;
- var then_scope: Scope.GenZIR = .{
- .parent = &cond_scope.base,
- .decl = cond_scope.decl,
- .arena = cond_scope.arena,
- .instructions = .{},
- };
- defer then_scope.instructions.deinit(mod.gpa);
-
- // Most result location types can be forwarded directly; however
- // if we need to write to a pointer which has an inferred type,
- // proper type inference requires peer type resolution on the while's
- // branches.
- const branch_rl: ResultLoc = switch (rl) {
- .discard, .none, .ty, .ptr, .ref => rl,
- .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = for_block },
- };
-
- var index_scope: Scope.LocalPtr = undefined;
- const then_sub_scope = blk: {
- const payload = for_node.payload.castTag(.PointerIndexPayload).?;
- const is_ptr = payload.ptr_token != null;
- const value_name = tree.tokenSlice(payload.value_symbol.firstToken());
- if (!mem.eql(u8, value_name, "_")) {
- return mod.failNode(&then_scope.base, payload.value_symbol, "TODO implement for value payload", .{});
- } else if (is_ptr) {
- return mod.failTok(&then_scope.base, payload.ptr_token.?, "pointer modifier invalid on discard", .{});
- }
-
- const index_symbol_node = payload.index_symbol orelse
- break :blk &then_scope.base;
-
- const index_name = tree.tokenSlice(index_symbol_node.firstToken());
- if (mem.eql(u8, index_name, "_")) {
- break :blk &then_scope.base;
- }
- // TODO make this const without an extra copy?
- index_scope = .{
- .parent = &then_scope.base,
- .gen_zir = &then_scope,
- .name = index_name,
- .ptr = index_ptr,
- };
- break :blk &index_scope.base;
- };
-
- const then_result = try expr(mod, then_sub_scope, branch_rl, for_node.body);
- if (!then_result.tag.isNoReturn()) {
- _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{
- .block = cond_block,
- .operand = then_result,
- }, .{});
- }
- condbr.positionals.then_body = .{
- .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
- };
-
- // else branch
- var else_scope: Scope.GenZIR = .{
- .parent = &cond_scope.base,
- .decl = cond_scope.decl,
- .arena = cond_scope.arena,
- .instructions = .{},
- };
- defer else_scope.instructions.deinit(mod.gpa);
-
- if (for_node.@"else") |else_node| {
- const else_src = tree.token_locs[else_node.body.lastToken()].start;
- const else_result = try expr(mod, &else_scope.base, branch_rl, else_node.body);
- if (!else_result.tag.isNoReturn()) {
- _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.Break, .{
- .block = for_block,
- .operand = else_result,
- }, .{});
- }
- } else {
- const else_src = tree.token_locs[for_node.lastToken()].start;
- _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.BreakVoid, .{
- .block = for_block,
- }, .{});
- }
- condbr.positionals.else_body = .{
- .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
- };
- return &for_block.base;
-}
-
-fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[cfe.ltoken].start;
- if (cfe.getRHS()) |rhs_node| {
- if (nodeMayNeedMemoryLocation(rhs_node)) {
- const ret_ptr = try addZIRNoOp(mod, scope, src, .ret_ptr);
- const operand = try expr(mod, scope, .{ .ptr = ret_ptr }, rhs_node);
- return addZIRUnOp(mod, scope, src, .@"return", operand);
- } else {
- const fn_ret_ty = try addZIRNoOp(mod, scope, src, .ret_type);
- const operand = try expr(mod, scope, .{ .ty = fn_ret_ty }, rhs_node);
- return addZIRUnOp(mod, scope, src, .@"return", operand);
- }
- } else {
- return addZIRNoOp(mod, scope, src, .returnvoid);
- }
-}
-
-fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneToken) InnerError!*zir.Inst {
- const tracy = trace(@src());
- defer tracy.end();
-
- const tree = scope.tree();
- const ident_name = try identifierTokenString(mod, scope, ident.token);
- const src = tree.token_locs[ident.token].start;
- if (mem.eql(u8, ident_name, "_")) {
- return mod.failNode(scope, &ident.base, "TODO implement '_' identifier", .{});
- }
-
- if (getSimplePrimitiveValue(ident_name)) |typed_value| {
- const result = try addZIRInstConst(mod, scope, src, typed_value);
- return rlWrap(mod, scope, rl, result);
- }
-
- if (ident_name.len >= 2) integer: {
- const first_c = ident_name[0];
- if (first_c == 'i' or first_c == 'u') {
- const is_signed = first_c == 'i';
- const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) {
- error.Overflow => return mod.failNode(
- scope,
- &ident.base,
- "primitive integer type '{}' exceeds maximum bit width of 65535",
- .{ident_name},
- ),
- error.InvalidCharacter => break :integer,
- };
- const val = switch (bit_count) {
- 8 => if (is_signed) Value.initTag(.i8_type) else Value.initTag(.u8_type),
- 16 => if (is_signed) Value.initTag(.i16_type) else Value.initTag(.u16_type),
- 32 => if (is_signed) Value.initTag(.i32_type) else Value.initTag(.u32_type),
- 64 => if (is_signed) Value.initTag(.i64_type) else Value.initTag(.u64_type),
- else => {
- const int_type_payload = try scope.arena().create(Value.Payload.IntType);
- int_type_payload.* = .{ .signed = is_signed, .bits = bit_count };
- const result = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initPayload(&int_type_payload.base),
- });
- return rlWrap(mod, scope, rl, result);
- },
- };
- const result = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = val,
- });
- return rlWrap(mod, scope, rl, result);
- }
- }
-
- // Local variables, including function parameters.
- {
- var s = scope;
- while (true) switch (s.tag) {
- .local_val => {
- const local_val = s.cast(Scope.LocalVal).?;
- if (mem.eql(u8, local_val.name, ident_name)) {
- return rlWrap(mod, scope, rl, local_val.inst);
- }
- s = local_val.parent;
- },
- .local_ptr => {
- const local_ptr = s.cast(Scope.LocalPtr).?;
- if (mem.eql(u8, local_ptr.name, ident_name)) {
- return rlWrapPtr(mod, scope, rl, local_ptr.ptr);
- }
- s = local_ptr.parent;
- },
- .gen_zir => s = s.cast(Scope.GenZIR).?.parent,
- else => break,
- };
- }
-
- if (mod.lookupDeclName(scope, ident_name)) |decl| {
- return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{}));
- }
-
- return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name});
-}
-
-fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
- const tree = scope.tree();
- const unparsed_bytes = tree.tokenSlice(str_lit.token);
- const arena = scope.arena();
-
- var bad_index: usize = undefined;
- const bytes = std.zig.parseStringLiteral(arena, unparsed_bytes, &bad_index) catch |err| switch (err) {
- error.InvalidCharacter => {
- const bad_byte = unparsed_bytes[bad_index];
- const src = tree.token_locs[str_lit.token].start;
- return mod.fail(scope, src + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
- },
- else => |e| return e,
- };
-
- const src = tree.token_locs[str_lit.token].start;
- return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
-}
-
-fn multilineStrLiteral(mod: *Module, scope: *Scope, node: *ast.Node.MultilineStringLiteral) !*zir.Inst {
- const tree = scope.tree();
- const lines = node.linesConst();
- const src = tree.token_locs[lines[0]].start;
-
- // line lengths and new lines
- var len = lines.len - 1;
- for (lines) |line| {
- // 2 for the '//' + 1 for '\n'
- len += tree.tokenSlice(line).len - 3;
- }
-
- const bytes = try scope.arena().alloc(u8, len);
- var i: usize = 0;
- for (lines) |line, line_i| {
- if (line_i != 0) {
- bytes[i] = '\n';
- i += 1;
- }
- const slice = tree.tokenSlice(line);
- mem.copy(u8, bytes[i..], slice[2 .. slice.len - 1]);
- i += slice.len - 3;
- }
-
- return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
-}
-
-fn charLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) !*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.token].start;
- const slice = tree.tokenSlice(node.token);
-
- var bad_index: usize = undefined;
- const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) {
- error.InvalidCharacter => {
- const bad_byte = slice[bad_index];
- return mod.fail(scope, src + bad_index, "invalid character: '{c}'\n", .{bad_byte});
- },
- };
-
- const int_payload = try scope.arena().create(Value.Payload.Int_u64);
- int_payload.* = .{ .int = value };
- return addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.comptime_int),
- .val = Value.initPayload(&int_payload.base),
- });
-}
-
-fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
- const arena = scope.arena();
- const tree = scope.tree();
- const prefixed_bytes = tree.tokenSlice(int_lit.token);
- const base = if (mem.startsWith(u8, prefixed_bytes, "0x"))
- 16
- else if (mem.startsWith(u8, prefixed_bytes, "0o"))
- 8
- else if (mem.startsWith(u8, prefixed_bytes, "0b"))
- 2
- else
- @as(u8, 10);
-
- const bytes = if (base == 10)
- prefixed_bytes
- else
- prefixed_bytes[2..];
-
- if (std.fmt.parseInt(u64, bytes, base)) |small_int| {
- const int_payload = try arena.create(Value.Payload.Int_u64);
- int_payload.* = .{ .int = small_int };
- const src = tree.token_locs[int_lit.token].start;
- return addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.comptime_int),
- .val = Value.initPayload(&int_payload.base),
- });
- } else |err| {
- return mod.failTok(scope, int_lit.token, "TODO implement int literals that don't fit in a u64", .{});
- }
-}
-
-fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
- const arena = scope.arena();
- const tree = scope.tree();
- const bytes = tree.tokenSlice(float_lit.token);
- if (bytes.len > 2 and bytes[1] == 'x') {
- return mod.failTok(scope, float_lit.token, "TODO hex floats", .{});
- }
-
- const val = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) {
- error.InvalidCharacter => unreachable, // validated by tokenizer
- };
- const float_payload = try arena.create(Value.Payload.Float_128);
- float_payload.* = .{ .val = val };
- const src = tree.token_locs[float_lit.token].start;
- return addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.comptime_float),
- .val = Value.initPayload(&float_payload.base),
- });
-}
-
-fn undefLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
- const arena = scope.arena();
- const tree = scope.tree();
- const src = tree.token_locs[node.token].start;
- return addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.@"undefined"),
- .val = Value.initTag(.undef),
- });
-}
-
-fn boolLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
- const arena = scope.arena();
- const tree = scope.tree();
- const src = tree.token_locs[node.token].start;
- return addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.bool),
- .val = switch (tree.token_ids[node.token]) {
- .Keyword_true => Value.initTag(.bool_true),
- .Keyword_false => Value.initTag(.bool_false),
- else => unreachable,
- },
- });
-}
-
-fn nullLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
- const arena = scope.arena();
- const tree = scope.tree();
- const src = tree.token_locs[node.token].start;
- return addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.@"null"),
- .val = Value.initTag(.null_value),
- });
-}
-
-fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst {
- if (asm_node.outputs.len != 0) {
- return mod.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{});
- }
- const arena = scope.arena();
- const tree = scope.tree();
-
- const inputs = try arena.alloc(*zir.Inst, asm_node.inputs.len);
- const args = try arena.alloc(*zir.Inst, asm_node.inputs.len);
-
- const src = tree.token_locs[asm_node.asm_token].start;
-
- const str_type = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.const_slice_u8_type),
- });
- const str_type_rl: ResultLoc = .{ .ty = str_type };
-
- for (asm_node.inputs) |input, i| {
- // TODO semantically analyze constraints
- inputs[i] = try expr(mod, scope, str_type_rl, input.constraint);
- args[i] = try expr(mod, scope, .none, input.expr);
- }
-
- const return_type = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.void_type),
- });
- const asm_inst = try addZIRInst(mod, scope, src, zir.Inst.Asm, .{
- .asm_source = try expr(mod, scope, str_type_rl, asm_node.template),
- .return_type = return_type,
- }, .{
- .@"volatile" = asm_node.volatile_token != null,
- //.clobbers = TODO handle clobbers
- .inputs = inputs,
- .args = args,
- });
- return asm_inst;
-}
-
-fn ensureBuiltinParamCount(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall, count: u32) !void {
- if (call.params_len == count)
- return;
-
- const s = if (count == 1) "" else "s";
- return mod.failTok(scope, call.builtin_token, "expected {} parameter{}, found {}", .{ count, s, call.params_len });
-}
-
-fn simpleCast(
- mod: *Module,
- scope: *Scope,
- rl: ResultLoc,
- call: *ast.Node.BuiltinCall,
- inst_tag: zir.Inst.Tag,
-) InnerError!*zir.Inst {
- try ensureBuiltinParamCount(mod, scope, call, 2);
- const tree = scope.tree();
- const src = tree.token_locs[call.builtin_token].start;
- const params = call.params();
- const dest_type = try typeExpr(mod, scope, params[0]);
- const rhs = try expr(mod, scope, .none, params[1]);
- const result = try addZIRBinOp(mod, scope, src, inst_tag, dest_type, rhs);
- return rlWrap(mod, scope, rl, result);
-}
-
-fn ptrToInt(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
- try ensureBuiltinParamCount(mod, scope, call, 1);
- const operand = try expr(mod, scope, .none, call.params()[0]);
- const tree = scope.tree();
- const src = tree.token_locs[call.builtin_token].start;
- return addZIRUnOp(mod, scope, src, .ptrtoint, operand);
-}
-
-fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
- try ensureBuiltinParamCount(mod, scope, call, 2);
- const tree = scope.tree();
- const src = tree.token_locs[call.builtin_token].start;
- const params = call.params();
- const dest_type = try typeExpr(mod, scope, params[0]);
- switch (rl) {
- .none => return try expr(mod, scope, .{ .ty = dest_type }, params[1]),
- .discard => {
- const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
- _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
- return result;
- },
- .ref => {
- const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
- return addZIRUnOp(mod, scope, result.src, .ref, result);
- },
- .ty => |result_ty| {
- const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
- return addZIRBinOp(mod, scope, src, .as, result_ty, result);
- },
- .ptr => |result_ptr| {
- const casted_result_ptr = try addZIRBinOp(mod, scope, src, .coerce_result_ptr, dest_type, result_ptr);
- return expr(mod, scope, .{ .ptr = casted_result_ptr }, params[1]);
- },
- .bitcasted_ptr => |bitcasted_ptr| {
- // TODO here we should be able to resolve the inference; we now have a type for the result.
- return mod.failTok(scope, call.builtin_token, "TODO implement @as with result location @bitCast", .{});
- },
- .inferred_ptr => |result_alloc| {
- // TODO here we should be able to resolve the inference; we now have a type for the result.
- return mod.failTok(scope, call.builtin_token, "TODO implement @as with inferred-type result location pointer", .{});
- },
- .block_ptr => |block_ptr| {
- const casted_block_ptr = try addZIRInst(mod, scope, src, zir.Inst.CoerceResultBlockPtr, .{
- .dest_type = dest_type,
- .block = block_ptr,
- }, .{});
- return expr(mod, scope, .{ .ptr = casted_block_ptr }, params[1]);
- },
- }
-}
-
-fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
- try ensureBuiltinParamCount(mod, scope, call, 2);
- const tree = scope.tree();
- const src = tree.token_locs[call.builtin_token].start;
- const params = call.params();
- const dest_type = try typeExpr(mod, scope, params[0]);
- switch (rl) {
- .none => {
- const operand = try expr(mod, scope, .none, params[1]);
- return addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
- },
- .discard => {
- const operand = try expr(mod, scope, .none, params[1]);
- const result = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
- _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
- return result;
- },
- .ref => {
- const operand = try expr(mod, scope, .ref, params[1]);
- const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand);
- return result;
- },
- .ty => |result_ty| {
- const result = try expr(mod, scope, .none, params[1]);
- const bitcasted = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, result);
- return addZIRBinOp(mod, scope, src, .as, result_ty, bitcasted);
- },
- .ptr => |result_ptr| {
- const casted_result_ptr = try addZIRUnOp(mod, scope, src, .bitcast_result_ptr, result_ptr);
- return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, params[1]);
- },
- .bitcasted_ptr => |bitcasted_ptr| {
- return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with result location another @bitCast", .{});
- },
- .block_ptr => |block_ptr| {
- return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with result location inferred peer types", .{});
- },
- .inferred_ptr => |result_alloc| {
- // TODO here we should be able to resolve the inference; we now have a type for the result.
- return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with inferred-type result location pointer", .{});
- },
- }
-}
-
-fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
- const tree = scope.tree();
- const builtin_name = tree.tokenSlice(call.builtin_token);
-
- // We handle the different builtins manually because they have different semantics depending
- // on the function. For example, `@as` and others participate in result location semantics,
- // and `@cImport` creates a special scope that collects a .c source code text buffer.
- // Also, some builtins have a variable number of parameters.
-
- if (mem.eql(u8, builtin_name, "@ptrToInt")) {
- return rlWrap(mod, scope, rl, try ptrToInt(mod, scope, call));
- } else if (mem.eql(u8, builtin_name, "@as")) {
- return as(mod, scope, rl, call);
- } else if (mem.eql(u8, builtin_name, "@floatCast")) {
- return simpleCast(mod, scope, rl, call, .floatcast);
- } else if (mem.eql(u8, builtin_name, "@intCast")) {
- return simpleCast(mod, scope, rl, call, .intcast);
- } else if (mem.eql(u8, builtin_name, "@bitCast")) {
- return bitCast(mod, scope, rl, call);
- } else if (mem.eql(u8, builtin_name, "@breakpoint")) {
- const src = tree.token_locs[call.builtin_token].start;
- return rlWrap(mod, scope, rl, try addZIRNoOp(mod, scope, src, .breakpoint));
- } else {
- return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{}'", .{builtin_name});
- }
-}
-
-fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) InnerError!*zir.Inst {
- const tree = scope.tree();
- const lhs = try expr(mod, scope, .none, node.lhs);
-
- const param_nodes = node.params();
- const args = try scope.getGenZIR().arena.alloc(*zir.Inst, param_nodes.len);
- for (param_nodes) |param_node, i| {
- const param_src = tree.token_locs[param_node.firstToken()].start;
- const param_type = try addZIRInst(mod, scope, param_src, zir.Inst.ParamType, .{
- .func = lhs,
- .arg_index = i,
- }, .{});
- args[i] = try expr(mod, scope, .{ .ty = param_type }, param_node);
- }
-
- const src = tree.token_locs[node.lhs.firstToken()].start;
- const result = try addZIRInst(mod, scope, src, zir.Inst.Call, .{
- .func = lhs,
- .args = args,
- }, .{});
- // TODO function call with result location
- return rlWrap(mod, scope, rl, result);
-}
-
-fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.OneToken) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[unreach_node.token].start;
- return addZIRNoOp(mod, scope, src, .@"unreachable");
-}
-
-fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {
- const simple_types = std.ComptimeStringMap(Value.Tag, .{
- .{ "u8", .u8_type },
- .{ "i8", .i8_type },
- .{ "isize", .isize_type },
- .{ "usize", .usize_type },
- .{ "c_short", .c_short_type },
- .{ "c_ushort", .c_ushort_type },
- .{ "c_int", .c_int_type },
- .{ "c_uint", .c_uint_type },
- .{ "c_long", .c_long_type },
- .{ "c_ulong", .c_ulong_type },
- .{ "c_longlong", .c_longlong_type },
- .{ "c_ulonglong", .c_ulonglong_type },
- .{ "c_longdouble", .c_longdouble_type },
- .{ "f16", .f16_type },
- .{ "f32", .f32_type },
- .{ "f64", .f64_type },
- .{ "f128", .f128_type },
- .{ "c_void", .c_void_type },
- .{ "bool", .bool_type },
- .{ "void", .void_type },
- .{ "type", .type_type },
- .{ "anyerror", .anyerror_type },
- .{ "comptime_int", .comptime_int_type },
- .{ "comptime_float", .comptime_float_type },
- .{ "noreturn", .noreturn_type },
- });
- if (simple_types.get(name)) |tag| {
- return TypedValue{
- .ty = Type.initTag(.type),
- .val = Value.initTag(tag),
- };
- }
- return null;
-}
-
-fn nodeMayNeedMemoryLocation(start_node: *ast.Node) bool {
- var node = start_node;
- while (true) {
- switch (node.tag) {
- .Root,
- .Use,
- .TestDecl,
- .DocComment,
- .SwitchCase,
- .SwitchElse,
- .Else,
- .Payload,
- .PointerPayload,
- .PointerIndexPayload,
- .ContainerField,
- .ErrorTag,
- .FieldInitializer,
- => unreachable,
-
- .Return,
- .Break,
- .Continue,
- .BitNot,
- .BoolNot,
- .VarDecl,
- .Defer,
- .AddressOf,
- .OptionalType,
- .Negation,
- .NegationWrap,
- .Resume,
- .ArrayType,
- .ArrayTypeSentinel,
- .PtrType,
- .SliceType,
- .Suspend,
- .AnyType,
- .ErrorType,
- .FnProto,
- .AnyFrameType,
- .IntegerLiteral,
- .FloatLiteral,
- .EnumLiteral,
- .StringLiteral,
- .MultilineStringLiteral,
- .CharLiteral,
- .BoolLiteral,
- .NullLiteral,
- .UndefinedLiteral,
- .Unreachable,
- .Identifier,
- .ErrorSetDecl,
- .ContainerDecl,
- .Asm,
- .Add,
- .AddWrap,
- .ArrayCat,
- .ArrayMult,
- .Assign,
- .AssignBitAnd,
- .AssignBitOr,
- .AssignBitShiftLeft,
- .AssignBitShiftRight,
- .AssignBitXor,
- .AssignDiv,
- .AssignSub,
- .AssignSubWrap,
- .AssignMod,
- .AssignAdd,
- .AssignAddWrap,
- .AssignMul,
- .AssignMulWrap,
- .BangEqual,
- .BitAnd,
- .BitOr,
- .BitShiftLeft,
- .BitShiftRight,
- .BitXor,
- .BoolAnd,
- .BoolOr,
- .Div,
- .EqualEqual,
- .ErrorUnion,
- .GreaterOrEqual,
- .GreaterThan,
- .LessOrEqual,
- .LessThan,
- .MergeErrorSets,
- .Mod,
- .Mul,
- .MulWrap,
- .Range,
- .Period,
- .Sub,
- .SubWrap,
- .Slice,
- .Deref,
- .ArrayAccess,
- .Block,
- => return false,
-
- // Forward the question to a sub-expression.
- .GroupedExpression => node = node.castTag(.GroupedExpression).?.expr,
- .Try => node = node.castTag(.Try).?.rhs,
- .Await => node = node.castTag(.Await).?.rhs,
- .Catch => node = node.castTag(.Catch).?.rhs,
- .OrElse => node = node.castTag(.OrElse).?.rhs,
- .Comptime => node = node.castTag(.Comptime).?.expr,
- .Nosuspend => node = node.castTag(.Nosuspend).?.expr,
- .UnwrapOptional => node = node.castTag(.UnwrapOptional).?.lhs,
-
- // True because these are exactly the expressions we need memory locations for.
- .ArrayInitializer,
- .ArrayInitializerDot,
- .StructInitializer,
- .StructInitializerDot,
- => return true,
-
- // True because depending on comptime conditions, sub-expressions
- // may be the kind that need memory locations.
- .While,
- .For,
- .Switch,
- .Call,
- .BuiltinCall, // TODO some of these can return false
- .LabeledBlock,
- => return true,
-
- // Depending on AST properties, they may need memory locations.
- .If => return node.castTag(.If).?.@"else" != null,
- }
- }
-}
-
-/// Applies `rl` semantics to `inst`. Expressions which do not do their own handling of
-/// result locations must call this function on their result.
-/// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer.
-/// If the `ResultLoc` is `ty`, it will coerce the result to the type.
-fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerError!*zir.Inst {
- switch (rl) {
- .none => return result,
- .discard => {
- // Emit a compile error for discarding error values.
- _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
- return result;
- },
- .ref => {
- // We need a pointer but we have a value.
- return addZIRUnOp(mod, scope, result.src, .ref, result);
- },
- .ty => |ty_inst| return addZIRBinOp(mod, scope, result.src, .as, ty_inst, result),
- .ptr => |ptr_inst| {
- const casted_result = try addZIRInst(mod, scope, result.src, zir.Inst.CoerceToPtrElem, .{
- .ptr = ptr_inst,
- .value = result,
- }, .{});
- _ = try addZIRBinOp(mod, scope, result.src, .store, ptr_inst, casted_result);
- return casted_result;
- },
- .bitcasted_ptr => |bitcasted_ptr| {
- return mod.fail(scope, result.src, "TODO implement rlWrap .bitcasted_ptr", .{});
- },
- .inferred_ptr => |alloc| {
- return mod.fail(scope, result.src, "TODO implement rlWrap .inferred_ptr", .{});
- },
- .block_ptr => |block_ptr| {
- return mod.fail(scope, result.src, "TODO implement rlWrap .block_ptr", .{});
- },
- }
-}
-
-fn rlWrapVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, result: void) InnerError!*zir.Inst {
- const src = scope.tree().token_locs[node.firstToken()].start;
- const void_inst = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.void),
- .val = Value.initTag(.void_value),
- });
- return rlWrap(mod, scope, rl, void_inst);
-}
-
-fn rlWrapPtr(mod: *Module, scope: *Scope, rl: ResultLoc, ptr: *zir.Inst) InnerError!*zir.Inst {
- if (rl == .ref) return ptr;
-
- return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, ptr.src, .deref, ptr));
-}
-
-pub fn addZIRInstSpecial(
- mod: *Module,
- scope: *Scope,
- src: usize,
- comptime T: type,
- positionals: std.meta.fieldInfo(T, "positionals").field_type,
- kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
-) !*T {
- const gen_zir = scope.getGenZIR();
- try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
- const inst = try gen_zir.arena.create(T);
- inst.* = .{
- .base = .{
- .tag = T.base_tag,
- .src = src,
- },
- .positionals = positionals,
- .kw_args = kw_args,
- };
- gen_zir.instructions.appendAssumeCapacity(&inst.base);
- return inst;
-}
-
-pub fn addZIRNoOpT(mod: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst.NoOp {
- const gen_zir = scope.getGenZIR();
- try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
- const inst = try gen_zir.arena.create(zir.Inst.NoOp);
- inst.* = .{
- .base = .{
- .tag = tag,
- .src = src,
- },
- .positionals = .{},
- .kw_args = .{},
- };
- gen_zir.instructions.appendAssumeCapacity(&inst.base);
- return inst;
-}
-
-pub fn addZIRNoOp(mod: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst {
- const inst = try addZIRNoOpT(mod, scope, src, tag);
- return &inst.base;
-}
-
-pub fn addZIRUnOp(
- mod: *Module,
- scope: *Scope,
- src: usize,
- tag: zir.Inst.Tag,
- operand: *zir.Inst,
-) !*zir.Inst {
- const gen_zir = scope.getGenZIR();
- try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
- const inst = try gen_zir.arena.create(zir.Inst.UnOp);
- inst.* = .{
- .base = .{
- .tag = tag,
- .src = src,
- },
- .positionals = .{
- .operand = operand,
- },
- .kw_args = .{},
- };
- gen_zir.instructions.appendAssumeCapacity(&inst.base);
- return &inst.base;
-}
-
-pub fn addZIRBinOp(
- mod: *Module,
- scope: *Scope,
- src: usize,
- tag: zir.Inst.Tag,
- lhs: *zir.Inst,
- rhs: *zir.Inst,
-) !*zir.Inst {
- const gen_zir = scope.getGenZIR();
- try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
- const inst = try gen_zir.arena.create(zir.Inst.BinOp);
- inst.* = .{
- .base = .{
- .tag = tag,
- .src = src,
- },
- .positionals = .{
- .lhs = lhs,
- .rhs = rhs,
- },
- .kw_args = .{},
- };
- gen_zir.instructions.appendAssumeCapacity(&inst.base);
- return &inst.base;
-}
-
-pub fn addZIRInstBlock(
- mod: *Module,
- scope: *Scope,
- src: usize,
- tag: zir.Inst.Tag,
- body: zir.Module.Body,
-) !*zir.Inst.Block {
- const gen_zir = scope.getGenZIR();
- try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
- const inst = try gen_zir.arena.create(zir.Inst.Block);
- inst.* = .{
- .base = .{
- .tag = tag,
- .src = src,
- },
- .positionals = .{
- .body = body,
- },
- .kw_args = .{},
- };
- gen_zir.instructions.appendAssumeCapacity(&inst.base);
- return inst;
-}
-
-pub fn addZIRInst(
- mod: *Module,
- scope: *Scope,
- src: usize,
- comptime T: type,
- positionals: std.meta.fieldInfo(T, "positionals").field_type,
- kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
-) !*zir.Inst {
- const inst_special = try addZIRInstSpecial(mod, scope, src, T, positionals, kw_args);
- return &inst_special.base;
-}
-
-/// TODO The existence of this function is a workaround for a bug in stage1.
-pub fn addZIRInstConst(mod: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*zir.Inst {
- const P = std.meta.fieldInfo(zir.Inst.Const, "positionals").field_type;
- return addZIRInst(mod, scope, src, zir.Inst.Const, P{ .typed_value = typed_value }, .{});
-}
-
-/// TODO The existence of this function is a workaround for a bug in stage1.
-pub fn addZIRInstLoop(mod: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Loop {
- const P = std.meta.fieldInfo(zir.Inst.Loop, "positionals").field_type;
- return addZIRInstSpecial(mod, scope, src, zir.Inst.Loop, P{ .body = body }, .{});
-}
diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig
deleted file mode 100644
index 255182908499a3b7c55484392bc7cf7a4b77965b..0000000000000000000000000000000000000000
--- a/src-self-hosted/clang.zig
+++ /dev/null
@@ -1,1197 +0,0 @@
-const builtin = @import("builtin");
-
-pub const struct_ZigClangConditionalOperator = @Type(.Opaque);
-pub const struct_ZigClangBinaryConditionalOperator = @Type(.Opaque);
-pub const struct_ZigClangAbstractConditionalOperator = @Type(.Opaque);
-pub const struct_ZigClangAPInt = @Type(.Opaque);
-pub const struct_ZigClangAPSInt = @Type(.Opaque);
-pub const struct_ZigClangAPFloat = @Type(.Opaque);
-pub const struct_ZigClangASTContext = @Type(.Opaque);
-pub const struct_ZigClangASTUnit = @Type(.Opaque);
-pub const struct_ZigClangArraySubscriptExpr = @Type(.Opaque);
-pub const struct_ZigClangArrayType = @Type(.Opaque);
-pub const struct_ZigClangAttributedType = @Type(.Opaque);
-pub const struct_ZigClangBinaryOperator = @Type(.Opaque);
-pub const struct_ZigClangBreakStmt = @Type(.Opaque);
-pub const struct_ZigClangBuiltinType = @Type(.Opaque);
-pub const struct_ZigClangCStyleCastExpr = @Type(.Opaque);
-pub const struct_ZigClangCallExpr = @Type(.Opaque);
-pub const struct_ZigClangCaseStmt = @Type(.Opaque);
-pub const struct_ZigClangCompoundAssignOperator = @Type(.Opaque);
-pub const struct_ZigClangCompoundStmt = @Type(.Opaque);
-pub const struct_ZigClangConstantArrayType = @Type(.Opaque);
-pub const struct_ZigClangContinueStmt = @Type(.Opaque);
-pub const struct_ZigClangDecayedType = @Type(.Opaque);
-pub const ZigClangDecl = @Type(.Opaque);
-pub const struct_ZigClangDeclRefExpr = @Type(.Opaque);
-pub const struct_ZigClangDeclStmt = @Type(.Opaque);
-pub const struct_ZigClangDefaultStmt = @Type(.Opaque);
-pub const struct_ZigClangDiagnosticOptions = @Type(.Opaque);
-pub const struct_ZigClangDiagnosticsEngine = @Type(.Opaque);
-pub const struct_ZigClangDoStmt = @Type(.Opaque);
-pub const struct_ZigClangElaboratedType = @Type(.Opaque);
-pub const struct_ZigClangEnumConstantDecl = @Type(.Opaque);
-pub const struct_ZigClangEnumDecl = @Type(.Opaque);
-pub const struct_ZigClangEnumType = @Type(.Opaque);
-pub const struct_ZigClangExpr = @Type(.Opaque);
-pub const struct_ZigClangFieldDecl = @Type(.Opaque);
-pub const struct_ZigClangFileID = @Type(.Opaque);
-pub const struct_ZigClangForStmt = @Type(.Opaque);
-pub const struct_ZigClangFullSourceLoc = @Type(.Opaque);
-pub const struct_ZigClangFunctionDecl = @Type(.Opaque);
-pub const struct_ZigClangFunctionProtoType = @Type(.Opaque);
-pub const struct_ZigClangIfStmt = @Type(.Opaque);
-pub const struct_ZigClangImplicitCastExpr = @Type(.Opaque);
-pub const struct_ZigClangIncompleteArrayType = @Type(.Opaque);
-pub const struct_ZigClangIntegerLiteral = @Type(.Opaque);
-pub const struct_ZigClangMacroDefinitionRecord = @Type(.Opaque);
-pub const struct_ZigClangMacroExpansion = @Type(.Opaque);
-pub const struct_ZigClangMacroQualifiedType = @Type(.Opaque);
-pub const struct_ZigClangMemberExpr = @Type(.Opaque);
-pub const struct_ZigClangNamedDecl = @Type(.Opaque);
-pub const struct_ZigClangNone = @Type(.Opaque);
-pub const struct_ZigClangOpaqueValueExpr = @Type(.Opaque);
-pub const struct_ZigClangPCHContainerOperations = @Type(.Opaque);
-pub const struct_ZigClangParenExpr = @Type(.Opaque);
-pub const struct_ZigClangParenType = @Type(.Opaque);
-pub const struct_ZigClangParmVarDecl = @Type(.Opaque);
-pub const struct_ZigClangPointerType = @Type(.Opaque);
-pub const struct_ZigClangPreprocessedEntity = @Type(.Opaque);
-pub const struct_ZigClangRecordDecl = @Type(.Opaque);
-pub const struct_ZigClangRecordType = @Type(.Opaque);
-pub const struct_ZigClangReturnStmt = @Type(.Opaque);
-pub const struct_ZigClangSkipFunctionBodiesScope = @Type(.Opaque);
-pub const struct_ZigClangSourceManager = @Type(.Opaque);
-pub const struct_ZigClangSourceRange = @Type(.Opaque);
-pub const ZigClangStmt = @Type(.Opaque);
-pub const struct_ZigClangStringLiteral = @Type(.Opaque);
-pub const struct_ZigClangStringRef = @Type(.Opaque);
-pub const struct_ZigClangSwitchStmt = @Type(.Opaque);
-pub const struct_ZigClangTagDecl = @Type(.Opaque);
-pub const struct_ZigClangType = @Type(.Opaque);
-pub const struct_ZigClangTypedefNameDecl = @Type(.Opaque);
-pub const struct_ZigClangTypedefType = @Type(.Opaque);
-pub const struct_ZigClangUnaryExprOrTypeTraitExpr = @Type(.Opaque);
-pub const struct_ZigClangUnaryOperator = @Type(.Opaque);
-pub const struct_ZigClangValueDecl = @Type(.Opaque);
-pub const struct_ZigClangVarDecl = @Type(.Opaque);
-pub const struct_ZigClangWhileStmt = @Type(.Opaque);
-pub const struct_ZigClangFunctionType = @Type(.Opaque);
-pub const struct_ZigClangPredefinedExpr = @Type(.Opaque);
-pub const struct_ZigClangInitListExpr = @Type(.Opaque);
-pub const ZigClangPreprocessingRecord = @Type(.Opaque);
-pub const ZigClangFloatingLiteral = @Type(.Opaque);
-pub const ZigClangConstantExpr = @Type(.Opaque);
-pub const ZigClangCharacterLiteral = @Type(.Opaque);
-pub const ZigClangStmtExpr = @Type(.Opaque);
-
-pub const ZigClangBO = extern enum {
- PtrMemD,
- PtrMemI,
- Mul,
- Div,
- Rem,
- Add,
- Sub,
- Shl,
- Shr,
- Cmp,
- LT,
- GT,
- LE,
- GE,
- EQ,
- NE,
- And,
- Xor,
- Or,
- LAnd,
- LOr,
- Assign,
- MulAssign,
- DivAssign,
- RemAssign,
- AddAssign,
- SubAssign,
- ShlAssign,
- ShrAssign,
- AndAssign,
- XorAssign,
- OrAssign,
- Comma,
-};
-
-pub const ZigClangUO = extern enum {
- PostInc,
- PostDec,
- PreInc,
- PreDec,
- AddrOf,
- Deref,
- Plus,
- Minus,
- Not,
- LNot,
- Real,
- Imag,
- Extension,
- Coawait,
-};
-
-pub const ZigClangTypeClass = extern enum {
- Adjusted,
- Decayed,
- ConstantArray,
- DependentSizedArray,
- IncompleteArray,
- VariableArray,
- Atomic,
- Attributed,
- BlockPointer,
- Builtin,
- Complex,
- Decltype,
- Auto,
- DeducedTemplateSpecialization,
- DependentAddressSpace,
- DependentName,
- DependentSizedExtVector,
- DependentTemplateSpecialization,
- DependentVector,
- Elaborated,
- FunctionNoProto,
- FunctionProto,
- InjectedClassName,
- MacroQualified,
- MemberPointer,
- ObjCObjectPointer,
- ObjCObject,
- ObjCInterface,
- ObjCTypeParam,
- PackExpansion,
- Paren,
- Pipe,
- Pointer,
- LValueReference,
- RValueReference,
- SubstTemplateTypeParmPack,
- SubstTemplateTypeParm,
- Enum,
- Record,
- TemplateSpecialization,
- TemplateTypeParm,
- TypeOfExpr,
- TypeOf,
- Typedef,
- UnaryTransform,
- UnresolvedUsing,
- Vector,
- ExtVector,
-};
-
-const ZigClangStmtClass = extern enum {
- NoStmtClass,
- GCCAsmStmtClass,
- MSAsmStmtClass,
- BreakStmtClass,
- CXXCatchStmtClass,
- CXXForRangeStmtClass,
- CXXTryStmtClass,
- CapturedStmtClass,
- CompoundStmtClass,
- ContinueStmtClass,
- CoreturnStmtClass,
- CoroutineBodyStmtClass,
- DeclStmtClass,
- DoStmtClass,
- ForStmtClass,
- GotoStmtClass,
- IfStmtClass,
- IndirectGotoStmtClass,
- MSDependentExistsStmtClass,
- NullStmtClass,
- OMPAtomicDirectiveClass,
- OMPBarrierDirectiveClass,
- OMPCancelDirectiveClass,
- OMPCancellationPointDirectiveClass,
- OMPCriticalDirectiveClass,
- OMPFlushDirectiveClass,
- OMPDistributeDirectiveClass,
- OMPDistributeParallelForDirectiveClass,
- OMPDistributeParallelForSimdDirectiveClass,
- OMPDistributeSimdDirectiveClass,
- OMPForDirectiveClass,
- OMPForSimdDirectiveClass,
- OMPMasterTaskLoopDirectiveClass,
- OMPMasterTaskLoopSimdDirectiveClass,
- OMPParallelForDirectiveClass,
- OMPParallelForSimdDirectiveClass,
- OMPParallelMasterTaskLoopDirectiveClass,
- OMPParallelMasterTaskLoopSimdDirectiveClass,
- OMPSimdDirectiveClass,
- OMPTargetParallelForSimdDirectiveClass,
- OMPTargetSimdDirectiveClass,
- OMPTargetTeamsDistributeDirectiveClass,
- OMPTargetTeamsDistributeParallelForDirectiveClass,
- OMPTargetTeamsDistributeParallelForSimdDirectiveClass,
- OMPTargetTeamsDistributeSimdDirectiveClass,
- OMPTaskLoopDirectiveClass,
- OMPTaskLoopSimdDirectiveClass,
- OMPTeamsDistributeDirectiveClass,
- OMPTeamsDistributeParallelForDirectiveClass,
- OMPTeamsDistributeParallelForSimdDirectiveClass,
- OMPTeamsDistributeSimdDirectiveClass,
- OMPMasterDirectiveClass,
- OMPOrderedDirectiveClass,
- OMPParallelDirectiveClass,
- OMPParallelMasterDirectiveClass,
- OMPParallelSectionsDirectiveClass,
- OMPSectionDirectiveClass,
- OMPSectionsDirectiveClass,
- OMPSingleDirectiveClass,
- OMPTargetDataDirectiveClass,
- OMPTargetDirectiveClass,
- OMPTargetEnterDataDirectiveClass,
- OMPTargetExitDataDirectiveClass,
- OMPTargetParallelDirectiveClass,
- OMPTargetParallelForDirectiveClass,
- OMPTargetTeamsDirectiveClass,
- OMPTargetUpdateDirectiveClass,
- OMPTaskDirectiveClass,
- OMPTaskgroupDirectiveClass,
- OMPTaskwaitDirectiveClass,
- OMPTaskyieldDirectiveClass,
- OMPTeamsDirectiveClass,
- ObjCAtCatchStmtClass,
- ObjCAtFinallyStmtClass,
- ObjCAtSynchronizedStmtClass,
- ObjCAtThrowStmtClass,
- ObjCAtTryStmtClass,
- ObjCAutoreleasePoolStmtClass,
- ObjCForCollectionStmtClass,
- ReturnStmtClass,
- SEHExceptStmtClass,
- SEHFinallyStmtClass,
- SEHLeaveStmtClass,
- SEHTryStmtClass,
- CaseStmtClass,
- DefaultStmtClass,
- SwitchStmtClass,
- AttributedStmtClass,
- BinaryConditionalOperatorClass,
- ConditionalOperatorClass,
- AddrLabelExprClass,
- ArrayInitIndexExprClass,
- ArrayInitLoopExprClass,
- ArraySubscriptExprClass,
- ArrayTypeTraitExprClass,
- AsTypeExprClass,
- AtomicExprClass,
- BinaryOperatorClass,
- CompoundAssignOperatorClass,
- BlockExprClass,
- CXXBindTemporaryExprClass,
- CXXBoolLiteralExprClass,
- CXXConstructExprClass,
- CXXTemporaryObjectExprClass,
- CXXDefaultArgExprClass,
- CXXDefaultInitExprClass,
- CXXDeleteExprClass,
- CXXDependentScopeMemberExprClass,
- CXXFoldExprClass,
- CXXInheritedCtorInitExprClass,
- CXXNewExprClass,
- CXXNoexceptExprClass,
- CXXNullPtrLiteralExprClass,
- CXXPseudoDestructorExprClass,
- CXXRewrittenBinaryOperatorClass,
- CXXScalarValueInitExprClass,
- CXXStdInitializerListExprClass,
- CXXThisExprClass,
- CXXThrowExprClass,
- CXXTypeidExprClass,
- CXXUnresolvedConstructExprClass,
- CXXUuidofExprClass,
- CallExprClass,
- CUDAKernelCallExprClass,
- CXXMemberCallExprClass,
- CXXOperatorCallExprClass,
- UserDefinedLiteralClass,
- BuiltinBitCastExprClass,
- CStyleCastExprClass,
- CXXFunctionalCastExprClass,
- CXXConstCastExprClass,
- CXXDynamicCastExprClass,
- CXXReinterpretCastExprClass,
- CXXStaticCastExprClass,
- ObjCBridgedCastExprClass,
- ImplicitCastExprClass,
- CharacterLiteralClass,
- ChooseExprClass,
- CompoundLiteralExprClass,
- ConceptSpecializationExprClass,
- ConvertVectorExprClass,
- CoawaitExprClass,
- CoyieldExprClass,
- DeclRefExprClass,
- DependentCoawaitExprClass,
- DependentScopeDeclRefExprClass,
- DesignatedInitExprClass,
- DesignatedInitUpdateExprClass,
- ExpressionTraitExprClass,
- ExtVectorElementExprClass,
- FixedPointLiteralClass,
- FloatingLiteralClass,
- ConstantExprClass,
- ExprWithCleanupsClass,
- FunctionParmPackExprClass,
- GNUNullExprClass,
- GenericSelectionExprClass,
- ImaginaryLiteralClass,
- ImplicitValueInitExprClass,
- InitListExprClass,
- IntegerLiteralClass,
- LambdaExprClass,
- MSPropertyRefExprClass,
- MSPropertySubscriptExprClass,
- MaterializeTemporaryExprClass,
- MemberExprClass,
- NoInitExprClass,
- OMPArraySectionExprClass,
- ObjCArrayLiteralClass,
- ObjCAvailabilityCheckExprClass,
- ObjCBoolLiteralExprClass,
- ObjCBoxedExprClass,
- ObjCDictionaryLiteralClass,
- ObjCEncodeExprClass,
- ObjCIndirectCopyRestoreExprClass,
- ObjCIsaExprClass,
- ObjCIvarRefExprClass,
- ObjCMessageExprClass,
- ObjCPropertyRefExprClass,
- ObjCProtocolExprClass,
- ObjCSelectorExprClass,
- ObjCStringLiteralClass,
- ObjCSubscriptRefExprClass,
- OffsetOfExprClass,
- OpaqueValueExprClass,
- UnresolvedLookupExprClass,
- UnresolvedMemberExprClass,
- PackExpansionExprClass,
- ParenExprClass,
- ParenListExprClass,
- PredefinedExprClass,
- PseudoObjectExprClass,
- RequiresExprClass,
- ShuffleVectorExprClass,
- SizeOfPackExprClass,
- SourceLocExprClass,
- StmtExprClass,
- StringLiteralClass,
- SubstNonTypeTemplateParmExprClass,
- SubstNonTypeTemplateParmPackExprClass,
- TypeTraitExprClass,
- TypoExprClass,
- UnaryExprOrTypeTraitExprClass,
- UnaryOperatorClass,
- VAArgExprClass,
- LabelStmtClass,
- WhileStmtClass,
-};
-
-pub const ZigClangCK = extern enum {
- Dependent,
- BitCast,
- LValueBitCast,
- LValueToRValueBitCast,
- LValueToRValue,
- NoOp,
- BaseToDerived,
- DerivedToBase,
- UncheckedDerivedToBase,
- Dynamic,
- ToUnion,
- ArrayToPointerDecay,
- FunctionToPointerDecay,
- NullToPointer,
- NullToMemberPointer,
- BaseToDerivedMemberPointer,
- DerivedToBaseMemberPointer,
- MemberPointerToBoolean,
- ReinterpretMemberPointer,
- UserDefinedConversion,
- ConstructorConversion,
- IntegralToPointer,
- PointerToIntegral,
- PointerToBoolean,
- ToVoid,
- VectorSplat,
- IntegralCast,
- IntegralToBoolean,
- IntegralToFloating,
- FixedPointCast,
- FixedPointToIntegral,
- IntegralToFixedPoint,
- FixedPointToBoolean,
- FloatingToIntegral,
- FloatingToBoolean,
- BooleanToSignedIntegral,
- FloatingCast,
- CPointerToObjCPointerCast,
- BlockPointerToObjCPointerCast,
- AnyPointerToBlockPointerCast,
- ObjCObjectLValueCast,
- FloatingRealToComplex,
- FloatingComplexToReal,
- FloatingComplexToBoolean,
- FloatingComplexCast,
- FloatingComplexToIntegralComplex,
- IntegralRealToComplex,
- IntegralComplexToReal,
- IntegralComplexToBoolean,
- IntegralComplexCast,
- IntegralComplexToFloatingComplex,
- ARCProduceObject,
- ARCConsumeObject,
- ARCReclaimReturnedObject,
- ARCExtendBlockObject,
- AtomicToNonAtomic,
- NonAtomicToAtomic,
- CopyAndAutoreleaseBlockObject,
- BuiltinFnToFnPtr,
- ZeroToOCLOpaqueType,
- AddressSpaceConversion,
- IntToOCLSampler,
-};
-
-pub const ZigClangAPValueKind = extern enum {
- None,
- Indeterminate,
- Int,
- Float,
- FixedPoint,
- ComplexInt,
- ComplexFloat,
- LValue,
- Vector,
- Array,
- Struct,
- Union,
- MemberPointer,
- AddrLabelDiff,
-};
-
-pub const ZigClangDeclKind = extern enum {
- AccessSpec,
- Block,
- Captured,
- ClassScopeFunctionSpecialization,
- Empty,
- Export,
- ExternCContext,
- FileScopeAsm,
- Friend,
- FriendTemplate,
- Import,
- LifetimeExtendedTemporary,
- LinkageSpec,
- Label,
- Namespace,
- NamespaceAlias,
- ObjCCompatibleAlias,
- ObjCCategory,
- ObjCCategoryImpl,
- ObjCImplementation,
- ObjCInterface,
- ObjCProtocol,
- ObjCMethod,
- ObjCProperty,
- BuiltinTemplate,
- Concept,
- ClassTemplate,
- FunctionTemplate,
- TypeAliasTemplate,
- VarTemplate,
- TemplateTemplateParm,
- Enum,
- Record,
- CXXRecord,
- ClassTemplateSpecialization,
- ClassTemplatePartialSpecialization,
- TemplateTypeParm,
- ObjCTypeParam,
- TypeAlias,
- Typedef,
- UnresolvedUsingTypename,
- Using,
- UsingDirective,
- UsingPack,
- UsingShadow,
- ConstructorUsingShadow,
- Binding,
- Field,
- ObjCAtDefsField,
- ObjCIvar,
- Function,
- CXXDeductionGuide,
- CXXMethod,
- CXXConstructor,
- CXXConversion,
- CXXDestructor,
- MSProperty,
- NonTypeTemplateParm,
- Var,
- Decomposition,
- ImplicitParam,
- OMPCapturedExpr,
- ParmVar,
- VarTemplateSpecialization,
- VarTemplatePartialSpecialization,
- EnumConstant,
- IndirectField,
- OMPDeclareMapper,
- OMPDeclareReduction,
- UnresolvedUsingValue,
- OMPAllocate,
- OMPRequires,
- OMPThreadPrivate,
- ObjCPropertyImpl,
- PragmaComment,
- PragmaDetectMismatch,
- RequiresExprBody,
- StaticAssert,
- TranslationUnit,
-};
-
-pub const ZigClangBuiltinTypeKind = extern enum {
- OCLImage1dRO,
- OCLImage1dArrayRO,
- OCLImage1dBufferRO,
- OCLImage2dRO,
- OCLImage2dArrayRO,
- OCLImage2dDepthRO,
- OCLImage2dArrayDepthRO,
- OCLImage2dMSAARO,
- OCLImage2dArrayMSAARO,
- OCLImage2dMSAADepthRO,
- OCLImage2dArrayMSAADepthRO,
- OCLImage3dRO,
- OCLImage1dWO,
- OCLImage1dArrayWO,
- OCLImage1dBufferWO,
- OCLImage2dWO,
- OCLImage2dArrayWO,
- OCLImage2dDepthWO,
- OCLImage2dArrayDepthWO,
- OCLImage2dMSAAWO,
- OCLImage2dArrayMSAAWO,
- OCLImage2dMSAADepthWO,
- OCLImage2dArrayMSAADepthWO,
- OCLImage3dWO,
- OCLImage1dRW,
- OCLImage1dArrayRW,
- OCLImage1dBufferRW,
- OCLImage2dRW,
- OCLImage2dArrayRW,
- OCLImage2dDepthRW,
- OCLImage2dArrayDepthRW,
- OCLImage2dMSAARW,
- OCLImage2dArrayMSAARW,
- OCLImage2dMSAADepthRW,
- OCLImage2dArrayMSAADepthRW,
- OCLImage3dRW,
- OCLIntelSubgroupAVCMcePayload,
- OCLIntelSubgroupAVCImePayload,
- OCLIntelSubgroupAVCRefPayload,
- OCLIntelSubgroupAVCSicPayload,
- OCLIntelSubgroupAVCMceResult,
- OCLIntelSubgroupAVCImeResult,
- OCLIntelSubgroupAVCRefResult,
- OCLIntelSubgroupAVCSicResult,
- OCLIntelSubgroupAVCImeResultSingleRefStreamout,
- OCLIntelSubgroupAVCImeResultDualRefStreamout,
- OCLIntelSubgroupAVCImeSingleRefStreamin,
- OCLIntelSubgroupAVCImeDualRefStreamin,
- SveInt8,
- SveInt16,
- SveInt32,
- SveInt64,
- SveUint8,
- SveUint16,
- SveUint32,
- SveUint64,
- SveFloat16,
- SveFloat32,
- SveFloat64,
- SveBool,
- Void,
- Bool,
- Char_U,
- UChar,
- WChar_U,
- Char8,
- Char16,
- Char32,
- UShort,
- UInt,
- ULong,
- ULongLong,
- UInt128,
- Char_S,
- SChar,
- WChar_S,
- Short,
- Int,
- Long,
- LongLong,
- Int128,
- ShortAccum,
- Accum,
- LongAccum,
- UShortAccum,
- UAccum,
- ULongAccum,
- ShortFract,
- Fract,
- LongFract,
- UShortFract,
- UFract,
- ULongFract,
- SatShortAccum,
- SatAccum,
- SatLongAccum,
- SatUShortAccum,
- SatUAccum,
- SatULongAccum,
- SatShortFract,
- SatFract,
- SatLongFract,
- SatUShortFract,
- SatUFract,
- SatULongFract,
- Half,
- Float,
- Double,
- LongDouble,
- Float16,
- Float128,
- NullPtr,
- ObjCId,
- ObjCClass,
- ObjCSel,
- OCLSampler,
- OCLEvent,
- OCLClkEvent,
- OCLQueue,
- OCLReserveID,
- Dependent,
- Overload,
- BoundMember,
- PseudoObject,
- UnknownAny,
- BuiltinFn,
- ARCUnbridgedCast,
- OMPArraySection,
-};
-
-pub const ZigClangCallingConv = extern enum {
- C,
- X86StdCall,
- X86FastCall,
- X86ThisCall,
- X86VectorCall,
- X86Pascal,
- Win64,
- X86_64SysV,
- X86RegCall,
- AAPCS,
- AAPCS_VFP,
- IntelOclBicc,
- SpirFunction,
- OpenCLKernel,
- Swift,
- PreserveMost,
- PreserveAll,
- AArch64VectorCall,
-};
-
-pub const ZigClangStorageClass = extern enum {
- None,
- Extern,
- Static,
- PrivateExtern,
- Auto,
- Register,
-};
-
-pub const ZigClangAPFloat_roundingMode = extern enum {
- NearestTiesToEven,
- TowardPositive,
- TowardNegative,
- TowardZero,
- NearestTiesToAway,
-};
-
-pub const ZigClangStringLiteral_StringKind = extern enum {
- Ascii,
- Wide,
- UTF8,
- UTF16,
- UTF32,
-};
-
-pub const ZigClangCharacterLiteral_CharacterKind = extern enum {
- Ascii,
- Wide,
- UTF8,
- UTF16,
- UTF32,
-};
-
-pub const ZigClangRecordDecl_field_iterator = extern struct {
- opaque: *c_void,
-};
-
-pub const ZigClangEnumDecl_enumerator_iterator = extern struct {
- opaque: *c_void,
-};
-
-pub const ZigClangPreprocessingRecord_iterator = extern struct {
- I: c_int,
- Self: *ZigClangPreprocessingRecord,
-};
-
-pub const ZigClangPreprocessedEntity_EntityKind = extern enum {
- InvalidKind,
- MacroExpansionKind,
- MacroDefinitionKind,
- InclusionDirectiveKind,
-};
-
-pub const ZigClangExpr_ConstExprUsage = extern enum {
- EvaluateForCodeGen,
- EvaluateForMangling,
-};
-
-pub const ZigClangUnaryExprOrTypeTrait_Kind = extern enum {
- SizeOf,
- AlignOf,
- VecStep,
- OpenMPRequiredSimdAlign,
- PreferredAlignOf,
-};
-
-pub extern fn ZigClangSourceManager_getSpellingLoc(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) struct_ZigClangSourceLocation;
-pub extern fn ZigClangSourceManager_getFilename(self: *const struct_ZigClangSourceManager, SpellingLoc: struct_ZigClangSourceLocation) ?[*:0]const u8;
-pub extern fn ZigClangSourceManager_getSpellingLineNumber(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) c_uint;
-pub extern fn ZigClangSourceManager_getSpellingColumnNumber(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) c_uint;
-pub extern fn ZigClangSourceManager_getCharacterData(self: ?*const struct_ZigClangSourceManager, SL: struct_ZigClangSourceLocation) [*:0]const u8;
-pub extern fn ZigClangASTContext_getPointerType(self: ?*const struct_ZigClangASTContext, T: struct_ZigClangQualType) struct_ZigClangQualType;
-pub extern fn ZigClangASTUnit_getASTContext(self: ?*struct_ZigClangASTUnit) ?*struct_ZigClangASTContext;
-pub extern fn ZigClangASTUnit_getSourceManager(self: *struct_ZigClangASTUnit) *struct_ZigClangSourceManager;
-pub extern fn ZigClangASTUnit_visitLocalTopLevelDecls(self: *struct_ZigClangASTUnit, context: ?*c_void, Fn: ?fn (?*c_void, *const ZigClangDecl) callconv(.C) bool) bool;
-pub extern fn ZigClangRecordType_getDecl(record_ty: ?*const struct_ZigClangRecordType) *const struct_ZigClangRecordDecl;
-pub extern fn ZigClangTagDecl_isThisDeclarationADefinition(self: *const ZigClangTagDecl) bool;
-pub extern fn ZigClangEnumType_getDecl(record_ty: ?*const struct_ZigClangEnumType) *const struct_ZigClangEnumDecl;
-pub extern fn ZigClangRecordDecl_getCanonicalDecl(record_decl: ?*const struct_ZigClangRecordDecl) ?*const struct_ZigClangTagDecl;
-pub extern fn ZigClangFieldDecl_getCanonicalDecl(field_decl: ?*const struct_ZigClangFieldDecl) ?*const struct_ZigClangFieldDecl;
-pub extern fn ZigClangFieldDecl_getAlignedAttribute(field_decl: ?*const struct_ZigClangFieldDecl, *const ZigClangASTContext) c_uint;
-pub extern fn ZigClangEnumDecl_getCanonicalDecl(self: ?*const struct_ZigClangEnumDecl) ?*const struct_ZigClangTagDecl;
-pub extern fn ZigClangTypedefNameDecl_getCanonicalDecl(self: ?*const struct_ZigClangTypedefNameDecl) ?*const struct_ZigClangTypedefNameDecl;
-pub extern fn ZigClangFunctionDecl_getCanonicalDecl(self: ?*const struct_ZigClangFunctionDecl) ?*const struct_ZigClangFunctionDecl;
-pub extern fn ZigClangParmVarDecl_getOriginalType(self: ?*const struct_ZigClangParmVarDecl) struct_ZigClangQualType;
-pub extern fn ZigClangVarDecl_getCanonicalDecl(self: ?*const struct_ZigClangVarDecl) ?*const struct_ZigClangVarDecl;
-pub extern fn ZigClangVarDecl_getSectionAttribute(self: *const ZigClangVarDecl, len: *usize) ?[*]const u8;
-pub extern fn ZigClangFunctionDecl_getAlignedAttribute(self: *const ZigClangFunctionDecl, *const ZigClangASTContext) c_uint;
-pub extern fn ZigClangVarDecl_getAlignedAttribute(self: *const ZigClangVarDecl, *const ZigClangASTContext) c_uint;
-pub extern fn ZigClangRecordDecl_getPackedAttribute(self: ?*const struct_ZigClangRecordDecl) bool;
-pub extern fn ZigClangRecordDecl_getDefinition(self: ?*const struct_ZigClangRecordDecl) ?*const struct_ZigClangRecordDecl;
-pub extern fn ZigClangEnumDecl_getDefinition(self: ?*const struct_ZigClangEnumDecl) ?*const struct_ZigClangEnumDecl;
-pub extern fn ZigClangRecordDecl_getLocation(self: ?*const struct_ZigClangRecordDecl) struct_ZigClangSourceLocation;
-pub extern fn ZigClangEnumDecl_getLocation(self: ?*const struct_ZigClangEnumDecl) struct_ZigClangSourceLocation;
-pub extern fn ZigClangTypedefNameDecl_getLocation(self: ?*const struct_ZigClangTypedefNameDecl) struct_ZigClangSourceLocation;
-pub extern fn ZigClangDecl_getLocation(self: *const ZigClangDecl) ZigClangSourceLocation;
-pub extern fn ZigClangRecordDecl_isUnion(record_decl: ?*const struct_ZigClangRecordDecl) bool;
-pub extern fn ZigClangRecordDecl_isStruct(record_decl: ?*const struct_ZigClangRecordDecl) bool;
-pub extern fn ZigClangRecordDecl_isAnonymousStructOrUnion(record_decl: ?*const struct_ZigClangRecordDecl) bool;
-pub extern fn ZigClangRecordDecl_field_begin(*const struct_ZigClangRecordDecl) ZigClangRecordDecl_field_iterator;
-pub extern fn ZigClangRecordDecl_field_end(*const struct_ZigClangRecordDecl) ZigClangRecordDecl_field_iterator;
-pub extern fn ZigClangRecordDecl_field_iterator_next(ZigClangRecordDecl_field_iterator) ZigClangRecordDecl_field_iterator;
-pub extern fn ZigClangRecordDecl_field_iterator_deref(ZigClangRecordDecl_field_iterator) *const struct_ZigClangFieldDecl;
-pub extern fn ZigClangRecordDecl_field_iterator_neq(ZigClangRecordDecl_field_iterator, ZigClangRecordDecl_field_iterator) bool;
-pub extern fn ZigClangEnumDecl_getIntegerType(self: ?*const struct_ZigClangEnumDecl) struct_ZigClangQualType;
-pub extern fn ZigClangEnumDecl_enumerator_begin(*const ZigClangEnumDecl) ZigClangEnumDecl_enumerator_iterator;
-pub extern fn ZigClangEnumDecl_enumerator_end(*const ZigClangEnumDecl) ZigClangEnumDecl_enumerator_iterator;
-pub extern fn ZigClangEnumDecl_enumerator_iterator_next(ZigClangEnumDecl_enumerator_iterator) ZigClangEnumDecl_enumerator_iterator;
-pub extern fn ZigClangEnumDecl_enumerator_iterator_deref(ZigClangEnumDecl_enumerator_iterator) *const ZigClangEnumConstantDecl;
-pub extern fn ZigClangEnumDecl_enumerator_iterator_neq(ZigClangEnumDecl_enumerator_iterator, ZigClangEnumDecl_enumerator_iterator) bool;
-pub extern fn ZigClangDecl_castToNamedDecl(decl: *const ZigClangDecl) ?*const ZigClangNamedDecl;
-pub extern fn ZigClangNamedDecl_getName_bytes_begin(decl: ?*const struct_ZigClangNamedDecl) [*:0]const u8;
-pub extern fn ZigClangSourceLocation_eq(a: struct_ZigClangSourceLocation, b: struct_ZigClangSourceLocation) bool;
-pub extern fn ZigClangTypedefType_getDecl(self: ?*const struct_ZigClangTypedefType) *const struct_ZigClangTypedefNameDecl;
-pub extern fn ZigClangTypedefNameDecl_getUnderlyingType(self: ?*const struct_ZigClangTypedefNameDecl) struct_ZigClangQualType;
-pub extern fn ZigClangQualType_getCanonicalType(self: struct_ZigClangQualType) struct_ZigClangQualType;
-pub extern fn ZigClangQualType_getTypeClass(self: struct_ZigClangQualType) ZigClangTypeClass;
-pub extern fn ZigClangQualType_getTypePtr(self: struct_ZigClangQualType) *const struct_ZigClangType;
-pub extern fn ZigClangQualType_addConst(self: *struct_ZigClangQualType) void;
-pub extern fn ZigClangQualType_eq(self: struct_ZigClangQualType, arg1: struct_ZigClangQualType) bool;
-pub extern fn ZigClangQualType_isConstQualified(self: struct_ZigClangQualType) bool;
-pub extern fn ZigClangQualType_isVolatileQualified(self: struct_ZigClangQualType) bool;
-pub extern fn ZigClangQualType_isRestrictQualified(self: struct_ZigClangQualType) bool;
-pub extern fn ZigClangType_getTypeClass(self: ?*const struct_ZigClangType) ZigClangTypeClass;
-pub extern fn ZigClangType_getPointeeType(self: ?*const struct_ZigClangType) struct_ZigClangQualType;
-pub extern fn ZigClangType_isVoidType(self: ?*const struct_ZigClangType) bool;
-pub extern fn ZigClangType_isConstantArrayType(self: ?*const struct_ZigClangType) bool;
-pub extern fn ZigClangType_isRecordType(self: ?*const struct_ZigClangType) bool;
-pub extern fn ZigClangType_isIncompleteOrZeroLengthArrayType(self: ?*const struct_ZigClangType, *const ZigClangASTContext) bool;
-pub extern fn ZigClangType_isArrayType(self: ?*const struct_ZigClangType) bool;
-pub extern fn ZigClangType_isBooleanType(self: ?*const struct_ZigClangType) bool;
-pub extern fn ZigClangType_getTypeClassName(self: *const struct_ZigClangType) [*:0]const u8;
-pub extern fn ZigClangType_getAsArrayTypeUnsafe(self: *const ZigClangType) *const ZigClangArrayType;
-pub extern fn ZigClangType_getAsRecordType(self: *const ZigClangType) ?*const ZigClangRecordType;
-pub extern fn ZigClangType_getAsUnionType(self: *const ZigClangType) ?*const ZigClangRecordType;
-pub extern fn ZigClangStmt_getBeginLoc(self: *const ZigClangStmt) struct_ZigClangSourceLocation;
-pub extern fn ZigClangStmt_getStmtClass(self: ?*const ZigClangStmt) ZigClangStmtClass;
-pub extern fn ZigClangStmt_classof_Expr(self: ?*const ZigClangStmt) bool;
-pub extern fn ZigClangExpr_getStmtClass(self: *const struct_ZigClangExpr) ZigClangStmtClass;
-pub extern fn ZigClangExpr_getType(self: *const struct_ZigClangExpr) struct_ZigClangQualType;
-pub extern fn ZigClangExpr_getBeginLoc(self: *const struct_ZigClangExpr) struct_ZigClangSourceLocation;
-pub extern fn ZigClangInitListExpr_getInit(self: ?*const struct_ZigClangInitListExpr, i: c_uint) *const ZigClangExpr;
-pub extern fn ZigClangInitListExpr_getArrayFiller(self: ?*const struct_ZigClangInitListExpr) *const ZigClangExpr;
-pub extern fn ZigClangInitListExpr_getNumInits(self: ?*const struct_ZigClangInitListExpr) c_uint;
-pub extern fn ZigClangInitListExpr_getInitializedFieldInUnion(self: ?*const struct_ZigClangInitListExpr) ?*ZigClangFieldDecl;
-pub extern fn ZigClangAPValue_getKind(self: ?*const struct_ZigClangAPValue) ZigClangAPValueKind;
-pub extern fn ZigClangAPValue_getInt(self: ?*const struct_ZigClangAPValue) *const struct_ZigClangAPSInt;
-pub extern fn ZigClangAPValue_getArrayInitializedElts(self: ?*const struct_ZigClangAPValue) c_uint;
-pub extern fn ZigClangAPValue_getArraySize(self: ?*const struct_ZigClangAPValue) c_uint;
-pub extern fn ZigClangAPValue_getLValueBase(self: ?*const struct_ZigClangAPValue) struct_ZigClangAPValueLValueBase;
-pub extern fn ZigClangAPSInt_isSigned(self: *const struct_ZigClangAPSInt) bool;
-pub extern fn ZigClangAPSInt_isNegative(self: *const struct_ZigClangAPSInt) bool;
-pub extern fn ZigClangAPSInt_negate(self: *const struct_ZigClangAPSInt) *const struct_ZigClangAPSInt;
-pub extern fn ZigClangAPSInt_free(self: *const struct_ZigClangAPSInt) void;
-pub extern fn ZigClangAPSInt_getRawData(self: *const struct_ZigClangAPSInt) [*:0]const u64;
-pub extern fn ZigClangAPSInt_getNumWords(self: *const struct_ZigClangAPSInt) c_uint;
-
-pub extern fn ZigClangAPInt_getLimitedValue(self: *const struct_ZigClangAPInt, limit: u64) u64;
-pub extern fn ZigClangAPValueLValueBase_dyn_cast_Expr(self: struct_ZigClangAPValueLValueBase) ?*const struct_ZigClangExpr;
-pub extern fn ZigClangASTUnit_delete(self: ?*struct_ZigClangASTUnit) void;
-
-pub extern fn ZigClangFunctionDecl_getType(self: *const ZigClangFunctionDecl) struct_ZigClangQualType;
-pub extern fn ZigClangFunctionDecl_getLocation(self: *const ZigClangFunctionDecl) struct_ZigClangSourceLocation;
-pub extern fn ZigClangFunctionDecl_hasBody(self: *const ZigClangFunctionDecl) bool;
-pub extern fn ZigClangFunctionDecl_getStorageClass(self: *const ZigClangFunctionDecl) ZigClangStorageClass;
-pub extern fn ZigClangFunctionDecl_getParamDecl(self: *const ZigClangFunctionDecl, i: c_uint) *const struct_ZigClangParmVarDecl;
-pub extern fn ZigClangFunctionDecl_getBody(self: *const ZigClangFunctionDecl) *const ZigClangStmt;
-pub extern fn ZigClangFunctionDecl_doesDeclarationForceExternallyVisibleDefinition(self: *const ZigClangFunctionDecl) bool;
-pub extern fn ZigClangFunctionDecl_isThisDeclarationADefinition(self: *const ZigClangFunctionDecl) bool;
-pub extern fn ZigClangFunctionDecl_doesThisDeclarationHaveABody(self: *const ZigClangFunctionDecl) bool;
-pub extern fn ZigClangFunctionDecl_isInlineSpecified(self: *const ZigClangFunctionDecl) bool;
-pub extern fn ZigClangFunctionDecl_isDefined(self: *const ZigClangFunctionDecl) bool;
-pub extern fn ZigClangFunctionDecl_getDefinition(self: *const ZigClangFunctionDecl) ?*const struct_ZigClangFunctionDecl;
-pub extern fn ZigClangFunctionDecl_getSectionAttribute(self: *const ZigClangFunctionDecl, len: *usize) ?[*]const u8;
-
-pub extern fn ZigClangBuiltinType_getKind(self: *const struct_ZigClangBuiltinType) ZigClangBuiltinTypeKind;
-
-pub extern fn ZigClangFunctionType_getNoReturnAttr(self: *const ZigClangFunctionType) bool;
-pub extern fn ZigClangFunctionType_getCallConv(self: *const ZigClangFunctionType) ZigClangCallingConv;
-pub extern fn ZigClangFunctionType_getReturnType(self: *const ZigClangFunctionType) ZigClangQualType;
-
-pub extern fn ZigClangFunctionProtoType_isVariadic(self: *const struct_ZigClangFunctionProtoType) bool;
-pub extern fn ZigClangFunctionProtoType_getNumParams(self: *const struct_ZigClangFunctionProtoType) c_uint;
-pub extern fn ZigClangFunctionProtoType_getParamType(self: *const struct_ZigClangFunctionProtoType, i: c_uint) ZigClangQualType;
-pub extern fn ZigClangFunctionProtoType_getReturnType(self: *const ZigClangFunctionProtoType) ZigClangQualType;
-
-pub const ZigClangSourceLocation = struct_ZigClangSourceLocation;
-pub const ZigClangQualType = struct_ZigClangQualType;
-pub const ZigClangConditionalOperator = struct_ZigClangConditionalOperator;
-pub const ZigClangBinaryConditionalOperator = struct_ZigClangBinaryConditionalOperator;
-pub const ZigClangAbstractConditionalOperator = struct_ZigClangAbstractConditionalOperator;
-pub const ZigClangAPValueLValueBase = struct_ZigClangAPValueLValueBase;
-pub const ZigClangAPValue = struct_ZigClangAPValue;
-pub const ZigClangAPSInt = struct_ZigClangAPSInt;
-pub const ZigClangAPFloat = struct_ZigClangAPFloat;
-pub const ZigClangASTContext = struct_ZigClangASTContext;
-pub const ZigClangASTUnit = struct_ZigClangASTUnit;
-pub const ZigClangArraySubscriptExpr = struct_ZigClangArraySubscriptExpr;
-pub const ZigClangArrayType = struct_ZigClangArrayType;
-pub const ZigClangAttributedType = struct_ZigClangAttributedType;
-pub const ZigClangBinaryOperator = struct_ZigClangBinaryOperator;
-pub const ZigClangBreakStmt = struct_ZigClangBreakStmt;
-pub const ZigClangBuiltinType = struct_ZigClangBuiltinType;
-pub const ZigClangCStyleCastExpr = struct_ZigClangCStyleCastExpr;
-pub const ZigClangCallExpr = struct_ZigClangCallExpr;
-pub const ZigClangCaseStmt = struct_ZigClangCaseStmt;
-pub const ZigClangCompoundAssignOperator = struct_ZigClangCompoundAssignOperator;
-pub const ZigClangCompoundStmt = struct_ZigClangCompoundStmt;
-pub const ZigClangConstantArrayType = struct_ZigClangConstantArrayType;
-pub const ZigClangContinueStmt = struct_ZigClangContinueStmt;
-pub const ZigClangDecayedType = struct_ZigClangDecayedType;
-pub const ZigClangDeclRefExpr = struct_ZigClangDeclRefExpr;
-pub const ZigClangDeclStmt = struct_ZigClangDeclStmt;
-pub const ZigClangDefaultStmt = struct_ZigClangDefaultStmt;
-pub const ZigClangDiagnosticOptions = struct_ZigClangDiagnosticOptions;
-pub const ZigClangDiagnosticsEngine = struct_ZigClangDiagnosticsEngine;
-pub const ZigClangDoStmt = struct_ZigClangDoStmt;
-pub const ZigClangElaboratedType = struct_ZigClangElaboratedType;
-pub const ZigClangEnumConstantDecl = struct_ZigClangEnumConstantDecl;
-pub const ZigClangEnumDecl = struct_ZigClangEnumDecl;
-pub const ZigClangEnumType = struct_ZigClangEnumType;
-pub const ZigClangExpr = struct_ZigClangExpr;
-pub const ZigClangFieldDecl = struct_ZigClangFieldDecl;
-pub const ZigClangFileID = struct_ZigClangFileID;
-pub const ZigClangForStmt = struct_ZigClangForStmt;
-pub const ZigClangFullSourceLoc = struct_ZigClangFullSourceLoc;
-pub const ZigClangFunctionDecl = struct_ZigClangFunctionDecl;
-pub const ZigClangFunctionProtoType = struct_ZigClangFunctionProtoType;
-pub const ZigClangIfStmt = struct_ZigClangIfStmt;
-pub const ZigClangImplicitCastExpr = struct_ZigClangImplicitCastExpr;
-pub const ZigClangIncompleteArrayType = struct_ZigClangIncompleteArrayType;
-pub const ZigClangIntegerLiteral = struct_ZigClangIntegerLiteral;
-pub const ZigClangMacroDefinitionRecord = struct_ZigClangMacroDefinitionRecord;
-pub const ZigClangMacroExpansion = struct_ZigClangMacroExpansion;
-pub const ZigClangMacroQualifiedType = struct_ZigClangMacroQualifiedType;
-pub const ZigClangMemberExpr = struct_ZigClangMemberExpr;
-pub const ZigClangNamedDecl = struct_ZigClangNamedDecl;
-pub const ZigClangNone = struct_ZigClangNone;
-pub const ZigClangOpaqueValueExpr = struct_ZigClangOpaqueValueExpr;
-pub const ZigClangPCHContainerOperations = struct_ZigClangPCHContainerOperations;
-pub const ZigClangParenExpr = struct_ZigClangParenExpr;
-pub const ZigClangParenType = struct_ZigClangParenType;
-pub const ZigClangParmVarDecl = struct_ZigClangParmVarDecl;
-pub const ZigClangPointerType = struct_ZigClangPointerType;
-pub const ZigClangPreprocessedEntity = struct_ZigClangPreprocessedEntity;
-pub const ZigClangRecordDecl = struct_ZigClangRecordDecl;
-pub const ZigClangRecordType = struct_ZigClangRecordType;
-pub const ZigClangReturnStmt = struct_ZigClangReturnStmt;
-pub const ZigClangSkipFunctionBodiesScope = struct_ZigClangSkipFunctionBodiesScope;
-pub const ZigClangSourceManager = struct_ZigClangSourceManager;
-pub const ZigClangSourceRange = struct_ZigClangSourceRange;
-pub const ZigClangStringLiteral = struct_ZigClangStringLiteral;
-pub const ZigClangStringRef = struct_ZigClangStringRef;
-pub const ZigClangSwitchStmt = struct_ZigClangSwitchStmt;
-pub const ZigClangTagDecl = struct_ZigClangTagDecl;
-pub const ZigClangType = struct_ZigClangType;
-pub const ZigClangTypedefNameDecl = struct_ZigClangTypedefNameDecl;
-pub const ZigClangTypedefType = struct_ZigClangTypedefType;
-pub const ZigClangUnaryExprOrTypeTraitExpr = struct_ZigClangUnaryExprOrTypeTraitExpr;
-pub const ZigClangUnaryOperator = struct_ZigClangUnaryOperator;
-pub const ZigClangValueDecl = struct_ZigClangValueDecl;
-pub const ZigClangVarDecl = struct_ZigClangVarDecl;
-pub const ZigClangWhileStmt = struct_ZigClangWhileStmt;
-pub const ZigClangFunctionType = struct_ZigClangFunctionType;
-pub const ZigClangPredefinedExpr = struct_ZigClangPredefinedExpr;
-pub const ZigClangInitListExpr = struct_ZigClangInitListExpr;
-
-pub const struct_ZigClangSourceLocation = extern struct {
- ID: c_uint,
-};
-
-pub const Stage2ErrorMsg = extern struct {
- filename_ptr: ?[*]const u8,
- filename_len: usize,
- msg_ptr: [*]const u8,
- msg_len: usize,
- // valid until the ASTUnit is freed
- source: ?[*]const u8,
- // 0 based
- line: c_uint,
- // 0 based
- column: c_uint,
- // byte offset into source
- offset: c_uint,
-};
-
-pub const struct_ZigClangQualType = extern struct {
- ptr: ?*c_void,
-};
-
-pub const struct_ZigClangAPValueLValueBase = extern struct {
- Ptr: ?*c_void,
- CallIndex: c_uint,
- Version: c_uint,
-};
-
-pub extern fn ZigClangErrorMsg_delete(ptr: [*]Stage2ErrorMsg, len: usize) void;
-
-pub extern fn ZigClangLoadFromCommandLine(
- args_begin: [*]?[*]const u8,
- args_end: [*]?[*]const u8,
- errors_ptr: *[*]Stage2ErrorMsg,
- errors_len: *usize,
- resources_path: [*:0]const u8,
-) ?*ZigClangASTUnit;
-
-pub extern fn ZigClangDecl_getKind(decl: *const ZigClangDecl) ZigClangDeclKind;
-pub extern fn ZigClangDecl_getDeclKindName(decl: *const ZigClangDecl) [*:0]const u8;
-
-pub const ZigClangCompoundStmt_const_body_iterator = [*]const *ZigClangStmt;
-
-pub extern fn ZigClangCompoundStmt_body_begin(self: *const ZigClangCompoundStmt) ZigClangCompoundStmt_const_body_iterator;
-pub extern fn ZigClangCompoundStmt_body_end(self: *const ZigClangCompoundStmt) ZigClangCompoundStmt_const_body_iterator;
-
-pub const ZigClangDeclStmt_const_decl_iterator = [*]const *ZigClangDecl;
-
-pub extern fn ZigClangDeclStmt_decl_begin(self: *const ZigClangDeclStmt) ZigClangDeclStmt_const_decl_iterator;
-pub extern fn ZigClangDeclStmt_decl_end(self: *const ZigClangDeclStmt) ZigClangDeclStmt_const_decl_iterator;
-
-pub extern fn ZigClangVarDecl_getLocation(self: *const struct_ZigClangVarDecl) ZigClangSourceLocation;
-pub extern fn ZigClangVarDecl_hasInit(self: *const struct_ZigClangVarDecl) bool;
-pub extern fn ZigClangVarDecl_getStorageClass(self: *const ZigClangVarDecl) ZigClangStorageClass;
-pub extern fn ZigClangVarDecl_getType(self: ?*const struct_ZigClangVarDecl) struct_ZigClangQualType;
-pub extern fn ZigClangVarDecl_getInit(*const ZigClangVarDecl) ?*const ZigClangExpr;
-pub extern fn ZigClangVarDecl_getTLSKind(self: ?*const struct_ZigClangVarDecl) ZigClangVarDecl_TLSKind;
-pub const ZigClangVarDecl_TLSKind = extern enum {
- None,
- Static,
- Dynamic,
-};
-
-pub extern fn ZigClangImplicitCastExpr_getBeginLoc(*const ZigClangImplicitCastExpr) ZigClangSourceLocation;
-pub extern fn ZigClangImplicitCastExpr_getCastKind(*const ZigClangImplicitCastExpr) ZigClangCK;
-pub extern fn ZigClangImplicitCastExpr_getSubExpr(*const ZigClangImplicitCastExpr) *const ZigClangExpr;
-
-pub extern fn ZigClangArrayType_getElementType(*const ZigClangArrayType) ZigClangQualType;
-pub extern fn ZigClangIncompleteArrayType_getElementType(*const ZigClangIncompleteArrayType) ZigClangQualType;
-
-pub extern fn ZigClangConstantArrayType_getElementType(self: *const struct_ZigClangConstantArrayType) ZigClangQualType;
-pub extern fn ZigClangConstantArrayType_getSize(self: *const struct_ZigClangConstantArrayType) *const struct_ZigClangAPInt;
-pub extern fn ZigClangDeclRefExpr_getDecl(*const ZigClangDeclRefExpr) *const ZigClangValueDecl;
-pub extern fn ZigClangDeclRefExpr_getFoundDecl(*const ZigClangDeclRefExpr) *const ZigClangNamedDecl;
-
-pub extern fn ZigClangParenType_getInnerType(*const ZigClangParenType) ZigClangQualType;
-
-pub extern fn ZigClangElaboratedType_getNamedType(*const ZigClangElaboratedType) ZigClangQualType;
-
-pub extern fn ZigClangAttributedType_getEquivalentType(*const ZigClangAttributedType) ZigClangQualType;
-
-pub extern fn ZigClangMacroQualifiedType_getModifiedType(*const ZigClangMacroQualifiedType) ZigClangQualType;
-
-pub extern fn ZigClangCStyleCastExpr_getBeginLoc(*const ZigClangCStyleCastExpr) ZigClangSourceLocation;
-pub extern fn ZigClangCStyleCastExpr_getSubExpr(*const ZigClangCStyleCastExpr) *const ZigClangExpr;
-pub extern fn ZigClangCStyleCastExpr_getType(*const ZigClangCStyleCastExpr) ZigClangQualType;
-
-pub const ZigClangExprEvalResult = struct_ZigClangExprEvalResult;
-pub const struct_ZigClangExprEvalResult = extern struct {
- HasSideEffects: bool,
- HasUndefinedBehavior: bool,
- SmallVectorImpl: ?*c_void,
- Val: ZigClangAPValue,
-};
-
-pub const struct_ZigClangAPValue = extern struct {
- Kind: ZigClangAPValueKind,
- Data: if (builtin.os.tag == .windows and builtin.abi == .msvc) [52]u8 else [68]u8,
-};
-pub extern fn ZigClangVarDecl_getTypeSourceInfo_getType(self: *const struct_ZigClangVarDecl) struct_ZigClangQualType;
-
-pub extern fn ZigClangIntegerLiteral_EvaluateAsInt(*const ZigClangIntegerLiteral, *ZigClangExprEvalResult, *const ZigClangASTContext) bool;
-pub extern fn ZigClangIntegerLiteral_getBeginLoc(*const ZigClangIntegerLiteral) ZigClangSourceLocation;
-pub extern fn ZigClangIntegerLiteral_isZero(*const ZigClangIntegerLiteral, *bool, *const ZigClangASTContext) bool;
-
-pub extern fn ZigClangReturnStmt_getRetValue(*const ZigClangReturnStmt) ?*const ZigClangExpr;
-
-pub extern fn ZigClangBinaryOperator_getOpcode(*const ZigClangBinaryOperator) ZigClangBO;
-pub extern fn ZigClangBinaryOperator_getBeginLoc(*const ZigClangBinaryOperator) ZigClangSourceLocation;
-pub extern fn ZigClangBinaryOperator_getLHS(*const ZigClangBinaryOperator) *const ZigClangExpr;
-pub extern fn ZigClangBinaryOperator_getRHS(*const ZigClangBinaryOperator) *const ZigClangExpr;
-pub extern fn ZigClangBinaryOperator_getType(*const ZigClangBinaryOperator) ZigClangQualType;
-
-pub extern fn ZigClangDecayedType_getDecayedType(*const ZigClangDecayedType) ZigClangQualType;
-
-pub extern fn ZigClangStringLiteral_getKind(*const ZigClangStringLiteral) ZigClangStringLiteral_StringKind;
-pub extern fn ZigClangStringLiteral_getString_bytes_begin_size(*const ZigClangStringLiteral, *usize) [*]const u8;
-
-pub extern fn ZigClangParenExpr_getSubExpr(*const ZigClangParenExpr) *const ZigClangExpr;
-
-pub extern fn ZigClangFieldDecl_isAnonymousStructOrUnion(*const struct_ZigClangFieldDecl) bool;
-pub extern fn ZigClangFieldDecl_isBitField(*const struct_ZigClangFieldDecl) bool;
-pub extern fn ZigClangFieldDecl_getType(*const struct_ZigClangFieldDecl) struct_ZigClangQualType;
-pub extern fn ZigClangFieldDecl_getLocation(*const struct_ZigClangFieldDecl) struct_ZigClangSourceLocation;
-
-pub extern fn ZigClangEnumConstantDecl_getInitExpr(*const ZigClangEnumConstantDecl) ?*const ZigClangExpr;
-pub extern fn ZigClangEnumConstantDecl_getInitVal(*const ZigClangEnumConstantDecl) *const ZigClangAPSInt;
-
-pub extern fn ZigClangASTUnit_getLocalPreprocessingEntities_begin(*ZigClangASTUnit) ZigClangPreprocessingRecord_iterator;
-pub extern fn ZigClangASTUnit_getLocalPreprocessingEntities_end(*ZigClangASTUnit) ZigClangPreprocessingRecord_iterator;
-pub extern fn ZigClangPreprocessingRecord_iterator_deref(ZigClangPreprocessingRecord_iterator) *ZigClangPreprocessedEntity;
-pub extern fn ZigClangPreprocessedEntity_getKind(*const ZigClangPreprocessedEntity) ZigClangPreprocessedEntity_EntityKind;
-
-pub extern fn ZigClangMacroDefinitionRecord_getName_getNameStart(*const ZigClangMacroDefinitionRecord) [*:0]const u8;
-pub extern fn ZigClangMacroDefinitionRecord_getSourceRange_getBegin(*const ZigClangMacroDefinitionRecord) ZigClangSourceLocation;
-pub extern fn ZigClangMacroDefinitionRecord_getSourceRange_getEnd(*const ZigClangMacroDefinitionRecord) ZigClangSourceLocation;
-
-pub extern fn ZigClangMacroExpansion_getDefinition(*const ZigClangMacroExpansion) *const ZigClangMacroDefinitionRecord;
-
-pub extern fn ZigClangIfStmt_getThen(*const ZigClangIfStmt) *const ZigClangStmt;
-pub extern fn ZigClangIfStmt_getElse(*const ZigClangIfStmt) ?*const ZigClangStmt;
-pub extern fn ZigClangIfStmt_getCond(*const ZigClangIfStmt) *const ZigClangStmt;
-
-pub extern fn ZigClangWhileStmt_getCond(*const ZigClangWhileStmt) *const ZigClangExpr;
-pub extern fn ZigClangWhileStmt_getBody(*const ZigClangWhileStmt) *const ZigClangStmt;
-
-pub extern fn ZigClangDoStmt_getCond(*const ZigClangDoStmt) *const ZigClangExpr;
-pub extern fn ZigClangDoStmt_getBody(*const ZigClangDoStmt) *const ZigClangStmt;
-
-pub extern fn ZigClangForStmt_getInit(*const ZigClangForStmt) ?*const ZigClangStmt;
-pub extern fn ZigClangForStmt_getCond(*const ZigClangForStmt) ?*const ZigClangExpr;
-pub extern fn ZigClangForStmt_getInc(*const ZigClangForStmt) ?*const ZigClangExpr;
-pub extern fn ZigClangForStmt_getBody(*const ZigClangForStmt) *const ZigClangStmt;
-
-pub extern fn ZigClangAPFloat_toString(self: *const ZigClangAPFloat, precision: c_uint, maxPadding: c_uint, truncateZero: bool) [*:0]const u8;
-pub extern fn ZigClangAPFloat_getValueAsApproximateDouble(*const ZigClangFloatingLiteral) f64;
-
-pub extern fn ZigClangAbstractConditionalOperator_getCond(*const ZigClangAbstractConditionalOperator) *const ZigClangExpr;
-pub extern fn ZigClangAbstractConditionalOperator_getTrueExpr(*const ZigClangAbstractConditionalOperator) *const ZigClangExpr;
-pub extern fn ZigClangAbstractConditionalOperator_getFalseExpr(*const ZigClangAbstractConditionalOperator) *const ZigClangExpr;
-
-pub extern fn ZigClangSwitchStmt_getConditionVariableDeclStmt(*const ZigClangSwitchStmt) ?*const ZigClangDeclStmt;
-pub extern fn ZigClangSwitchStmt_getCond(*const ZigClangSwitchStmt) *const ZigClangExpr;
-pub extern fn ZigClangSwitchStmt_getBody(*const ZigClangSwitchStmt) *const ZigClangStmt;
-pub extern fn ZigClangSwitchStmt_isAllEnumCasesCovered(*const ZigClangSwitchStmt) bool;
-
-pub extern fn ZigClangCaseStmt_getLHS(*const ZigClangCaseStmt) *const ZigClangExpr;
-pub extern fn ZigClangCaseStmt_getRHS(*const ZigClangCaseStmt) ?*const ZigClangExpr;
-pub extern fn ZigClangCaseStmt_getBeginLoc(*const ZigClangCaseStmt) ZigClangSourceLocation;
-pub extern fn ZigClangCaseStmt_getSubStmt(*const ZigClangCaseStmt) *const ZigClangStmt;
-
-pub extern fn ZigClangDefaultStmt_getSubStmt(*const ZigClangDefaultStmt) *const ZigClangStmt;
-
-pub extern fn ZigClangExpr_EvaluateAsConstantExpr(*const ZigClangExpr, *ZigClangExprEvalResult, ZigClangExpr_ConstExprUsage, *const ZigClangASTContext) bool;
-
-pub extern fn ZigClangPredefinedExpr_getFunctionName(*const ZigClangPredefinedExpr) *const ZigClangStringLiteral;
-
-pub extern fn ZigClangCharacterLiteral_getBeginLoc(*const ZigClangCharacterLiteral) ZigClangSourceLocation;
-pub extern fn ZigClangCharacterLiteral_getKind(*const ZigClangCharacterLiteral) ZigClangCharacterLiteral_CharacterKind;
-pub extern fn ZigClangCharacterLiteral_getValue(*const ZigClangCharacterLiteral) c_uint;
-
-pub extern fn ZigClangStmtExpr_getSubStmt(*const ZigClangStmtExpr) *const ZigClangCompoundStmt;
-
-pub extern fn ZigClangMemberExpr_getBase(*const ZigClangMemberExpr) *const ZigClangExpr;
-pub extern fn ZigClangMemberExpr_isArrow(*const ZigClangMemberExpr) bool;
-pub extern fn ZigClangMemberExpr_getMemberDecl(*const ZigClangMemberExpr) *const ZigClangValueDecl;
-
-pub extern fn ZigClangArraySubscriptExpr_getBase(*const ZigClangArraySubscriptExpr) *const ZigClangExpr;
-pub extern fn ZigClangArraySubscriptExpr_getIdx(*const ZigClangArraySubscriptExpr) *const ZigClangExpr;
-
-pub extern fn ZigClangCallExpr_getCallee(*const ZigClangCallExpr) *const ZigClangExpr;
-pub extern fn ZigClangCallExpr_getNumArgs(*const ZigClangCallExpr) c_uint;
-pub extern fn ZigClangCallExpr_getArgs(*const ZigClangCallExpr) [*]const *const ZigClangExpr;
-
-pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangQualType;
-pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangSourceLocation;
-pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getKind(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangUnaryExprOrTypeTrait_Kind;
-
-pub extern fn ZigClangUnaryOperator_getOpcode(*const ZigClangUnaryOperator) ZigClangUO;
-pub extern fn ZigClangUnaryOperator_getType(*const ZigClangUnaryOperator) ZigClangQualType;
-pub extern fn ZigClangUnaryOperator_getSubExpr(*const ZigClangUnaryOperator) *const ZigClangExpr;
-pub extern fn ZigClangUnaryOperator_getBeginLoc(*const ZigClangUnaryOperator) ZigClangSourceLocation;
-
-pub extern fn ZigClangOpaqueValueExpr_getSourceExpr(*const ZigClangOpaqueValueExpr) ?*const ZigClangExpr;
-
-pub extern fn ZigClangCompoundAssignOperator_getType(*const ZigClangCompoundAssignOperator) ZigClangQualType;
-pub extern fn ZigClangCompoundAssignOperator_getComputationLHSType(*const ZigClangCompoundAssignOperator) ZigClangQualType;
-pub extern fn ZigClangCompoundAssignOperator_getComputationResultType(*const ZigClangCompoundAssignOperator) ZigClangQualType;
-pub extern fn ZigClangCompoundAssignOperator_getBeginLoc(*const ZigClangCompoundAssignOperator) ZigClangSourceLocation;
-pub extern fn ZigClangCompoundAssignOperator_getOpcode(*const ZigClangCompoundAssignOperator) ZigClangBO;
-pub extern fn ZigClangCompoundAssignOperator_getLHS(*const ZigClangCompoundAssignOperator) *const ZigClangExpr;
-pub extern fn ZigClangCompoundAssignOperator_getRHS(*const ZigClangCompoundAssignOperator) *const ZigClangExpr;
diff --git a/src-self-hosted/clang_options.zig b/src-self-hosted/clang_options.zig
deleted file mode 100644
index 1b70c71dac990901258be3bf0aceae2cdc88b209..0000000000000000000000000000000000000000
--- a/src-self-hosted/clang_options.zig
+++ /dev/null
@@ -1,136 +0,0 @@
-const std = @import("std");
-const mem = std.mem;
-
-pub const list = @import("clang_options_data.zig").data;
-
-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,
-
- /// Prefixed by "-"
- pd1: bool = false,
-
- /// Prefixed by "--"
- pd2: bool = false,
-
- /// Prefixed by "/"
- psl: bool = false,
-
- pub const Syntax = union(enum) {
- /// A flag with no values.
- flag,
-
- /// An option which prefixes its (single) value.
- joined,
-
- /// An option which is followed by its value.
- separate,
-
- /// An option which is either joined to its (non-empty) value, or followed by its value.
- joined_or_separate,
-
- /// An option which is both joined to its (first) value, and followed by its (second) value.
- joined_and_separate,
-
- /// An option followed by its values, which are separated by commas.
- comma_joined,
-
- /// An option which consumes an optional joined argument and any other remaining arguments.
- remaining_args_joined,
-
- /// An option which is which takes multiple (separate) arguments.
- multi_arg: u8,
- };
-
- pub fn matchEql(self: CliArg, arg: []const u8) u2 {
- if (self.pd1 and arg.len >= self.name.len + 1 and
- mem.startsWith(u8, arg, "-") and mem.eql(u8, arg[1..], self.name))
- {
- return 1;
- }
- if (self.pd2 and arg.len >= self.name.len + 2 and
- mem.startsWith(u8, arg, "--") and mem.eql(u8, arg[2..], self.name))
- {
- return 2;
- }
- if (self.psl and arg.len >= self.name.len + 1 and
- mem.startsWith(u8, arg, "/") and mem.eql(u8, arg[1..], self.name))
- {
- return 1;
- }
- return 0;
- }
-
- pub fn matchStartsWith(self: CliArg, arg: []const u8) usize {
- if (self.pd1 and arg.len >= self.name.len + 1 and
- mem.startsWith(u8, arg, "-") and mem.startsWith(u8, arg[1..], self.name))
- {
- return self.name.len + 1;
- }
- if (self.pd2 and arg.len >= self.name.len + 2 and
- mem.startsWith(u8, arg, "--") and mem.startsWith(u8, arg[2..], self.name))
- {
- return self.name.len + 2;
- }
- if (self.psl and arg.len >= self.name.len + 1 and
- mem.startsWith(u8, arg, "/") and mem.startsWith(u8, arg[1..], self.name))
- {
- return self.name.len + 1;
- }
- return 0;
- }
-};
-
-/// Shortcut function for initializing a `CliArg`
-pub fn flagpd1(name: []const u8) CliArg {
- return .{
- .name = name,
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- };
-}
-
-/// Shortcut function for initializing a `CliArg`
-pub fn flagpsl(name: []const u8) CliArg {
- return .{
- .name = name,
- .syntax = .flag,
- .zig_equivalent = .other,
- .psl = true,
- };
-}
-
-/// Shortcut function for initializing a `CliArg`
-pub fn joinpd1(name: []const u8) CliArg {
- return .{
- .name = name,
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- };
-}
-
-/// Shortcut function for initializing a `CliArg`
-pub fn jspd1(name: []const u8) CliArg {
- return .{
- .name = name,
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- };
-}
-
-/// Shortcut function for initializing a `CliArg`
-pub fn sepd1(name: []const u8) CliArg {
- return .{
- .name = name,
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = true,
- };
-}
diff --git a/src-self-hosted/clang_options_data.zig b/src-self-hosted/clang_options_data.zig
deleted file mode 100644
index 889737bdac6020f99333755b321bd664a2818d26..0000000000000000000000000000000000000000
--- a/src-self-hosted/clang_options_data.zig
+++ /dev/null
@@ -1,5870 +0,0 @@
-// This file is generated by tools/update_clang_options.zig.
-// zig fmt: off
-usingnamespace @import("clang_options.zig");
-pub const data = blk: { @setEvalBranchQuota(6000); break :blk &[_]CliArg{
-flagpd1("C"),
-flagpd1("CC"),
-.{
- .name = "E",
- .syntax = .flag,
- .zig_equivalent = .pp_or_asm,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("EB"),
-flagpd1("EL"),
-flagpd1("Eonly"),
-flagpd1("H"),
-.{
- .name = "",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("I-"),
-flagpd1("M"),
-.{
- .name = "MD",
- .syntax = .flag,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "MG",
- .syntax = .flag,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "MM",
- .syntax = .flag,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "MMD",
- .syntax = .flag,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "MP",
- .syntax = .flag,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "MV",
- .syntax = .flag,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("Mach"),
-flagpd1("O0"),
-flagpd1("O4"),
-.{
- .name = "O",
- .syntax = .flag,
- .zig_equivalent = .optimize,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("ObjC"),
-flagpd1("ObjC++"),
-flagpd1("P"),
-flagpd1("Q"),
-flagpd1("Qn"),
-flagpd1("Qunused-arguments"),
-flagpd1("Qy"),
-.{
- .name = "S",
- .syntax = .flag,
- .zig_equivalent = .pp_or_asm,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("WCL4"),
-flagpd1("Wall"),
-flagpd1("Wdeprecated"),
-flagpd1("Wlarge-by-value-copy"),
-flagpd1("Wno-deprecated"),
-flagpd1("Wno-rewrite-macros"),
-flagpd1("Wno-write-strings"),
-flagpd1("Wwrite-strings"),
-flagpd1("X"),
-sepd1("Xanalyzer"),
-sepd1("Xassembler"),
-sepd1("Xclang"),
-sepd1("Xcuda-fatbinary"),
-sepd1("Xcuda-ptxas"),
-.{
- .name = "Xlinker",
- .syntax = .separate,
- .zig_equivalent = .for_linker,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-sepd1("Xopenmp-target"),
-sepd1("Xpreprocessor"),
-flagpd1("Z"),
-flagpd1("Z-Xlinker-no-demangle"),
-flagpd1("Z-reserved-lib-cckext"),
-flagpd1("Z-reserved-lib-stdc++"),
-sepd1("Zlinker-input"),
-.{
- .name = "CLASSPATH",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "###",
- .syntax = .flag,
- .zig_equivalent = .verbose_cmds,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "Brepro",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Brepro-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Bt",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Bt+",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "C",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "E",
- .syntax = .flag,
- .zig_equivalent = .pp_or_asm,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "EP",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "FA",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "FC",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "FS",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Fx",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "G1",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "G2",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GA",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GF",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GF-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GH",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GL",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GL-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GR",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GR-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GS",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GS-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GT",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GX",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GX-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GZ",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gd",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Ge",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gh",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gm",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gm-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gr",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gregcall",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gv",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gw",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gw-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gy",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gy-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gz",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "H",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "HELP",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "J",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "JMC",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "LD",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "LDd",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "LN",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "MD",
- .syntax = .flag,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "MDd",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-flagpsl("MT"),
-.{
- .name = "MTd",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "P",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "QIfist",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "?",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Qfast_transcendentals",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Qimprecise_fwaits",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Qpar",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Qsafe_fp_loads",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Qspectre",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Qvec",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Qvec-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "TC",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "TP",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "V",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "W0",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "W1",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "W2",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "W3",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "W4",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "WL",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "WX",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "WX-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Wall",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Wp64",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "X",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Y-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Yd",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Z7",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "ZH:MD5",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "ZH:SHA1",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "ZH:SHA_256",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "ZI",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Za",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:__cplusplus",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:alignedNew",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:alignedNew-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:auto",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:char8_t",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:char8_t-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:dllexportInlines",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:dllexportInlines-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:forScope",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:inline",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:rvalueCast",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:sizedDealloc",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:sizedDealloc-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:strictStrings",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:ternary",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:threadSafeInit",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:threadSafeInit-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:trigraphs",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:trigraphs-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:twoPhase",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:twoPhase-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:wchar_t",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zd",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Ze",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zg",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zi",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zl",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zo",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zo-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zp",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zs",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "analyze-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "await",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "bigobj",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "c",
- .syntax = .flag,
- .zig_equivalent = .c,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "d1PP",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "d1reportAllClassLayout",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "d2FastFail",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "d2Zi+",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "diagnostics:caret",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "diagnostics:classic",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "diagnostics:column",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "fallback",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "fp:except",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "fp:except-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "fp:fast",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "fp:precise",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "fp:strict",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "help",
- .syntax = .flag,
- .zig_equivalent = .driver_punt,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "homeparams",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "hotpatch",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "kernel",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "kernel-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "nologo",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "openmp",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "openmp-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "openmp:experimental",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "permissive-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "sdl",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "sdl-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "showFilenames",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "showFilenames-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "showIncludes",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "u",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "utf-8",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "validate-charset",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "validate-charset-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "vmb",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "vmg",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "vmm",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "vms",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "vmv",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "volatile:iso",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "volatile:ms",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "w",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "wd4005",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "wd4018",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "wd4100",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "wd4910",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "wd4996",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "all-warnings",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "analyze",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "analyzer-no-default-checks",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "assemble",
- .syntax = .flag,
- .zig_equivalent = .pp_or_asm,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "assert",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "bootclasspath",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "classpath",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "comments",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "comments-in-macros",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "compile",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "constant-cfstrings",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "debug",
- .syntax = .flag,
- .zig_equivalent = .debug,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "define-macro",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "dependencies",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "dyld-prefix",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "encoding",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "entry",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "extdirs",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "extra-warnings",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "for-linker",
- .syntax = .separate,
- .zig_equivalent = .for_linker,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "force-link",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "help-hidden",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include-barrier",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include-directory",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include-directory-after",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include-prefix",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include-with-prefix",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include-with-prefix-after",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include-with-prefix-before",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "language",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "library-directory",
- .syntax = .separate,
- .zig_equivalent = .lib_dir,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "mhwdiv",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "migrate",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "no-line-commands",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "no-standard-includes",
- .syntax = .flag,
- .zig_equivalent = .nostdlibinc,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "no-standard-libraries",
- .syntax = .flag,
- .zig_equivalent = .nostdlib,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "no-undefined",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "no-warnings",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "optimize",
- .syntax = .flag,
- .zig_equivalent = .optimize,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "output",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "output-class-directory",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "param",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "precompile",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "prefix",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "preprocess",
- .syntax = .flag,
- .zig_equivalent = .pp_or_asm,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "print-diagnostic-categories",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "print-file-name",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "print-missing-file-dependencies",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "print-prog-name",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "profile",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "profile-blocks",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "resource",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "rtlib",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "serialize-diagnostics",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "signed-char",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "std",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "stdlib",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "sysroot",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "target-help",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "trace-includes",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "undefine-macro",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "unsigned-char",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "user-dependencies",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "verbose",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "version",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "write-dependencies",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "write-user-dependencies",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-sepd1("add-plugin"),
-flagpd1("faggressive-function-elimination"),
-flagpd1("fno-aggressive-function-elimination"),
-flagpd1("falign-commons"),
-flagpd1("fno-align-commons"),
-flagpd1("falign-jumps"),
-flagpd1("fno-align-jumps"),
-flagpd1("falign-labels"),
-flagpd1("fno-align-labels"),
-flagpd1("falign-loops"),
-flagpd1("fno-align-loops"),
-flagpd1("faligned-alloc-unavailable"),
-flagpd1("all_load"),
-flagpd1("fall-intrinsics"),
-flagpd1("fno-all-intrinsics"),
-sepd1("allowable_client"),
-flagpd1("cfg-add-implicit-dtors"),
-flagpd1("unoptimized-cfg"),
-flagpd1("analyze"),
-sepd1("analyze-function"),
-sepd1("analyzer-checker"),
-flagpd1("analyzer-checker-help"),
-flagpd1("analyzer-checker-help-alpha"),
-flagpd1("analyzer-checker-help-developer"),
-flagpd1("analyzer-checker-option-help"),
-flagpd1("analyzer-checker-option-help-alpha"),
-flagpd1("analyzer-checker-option-help-developer"),
-sepd1("analyzer-config"),
-sepd1("analyzer-config-compatibility-mode"),
-flagpd1("analyzer-config-help"),
-sepd1("analyzer-constraints"),
-flagpd1("analyzer-disable-all-checks"),
-sepd1("analyzer-disable-checker"),
-flagpd1("analyzer-disable-retry-exhausted"),
-flagpd1("analyzer-display-progress"),
-sepd1("analyzer-dump-egraph"),
-sepd1("analyzer-inline-max-stack-depth"),
-sepd1("analyzer-inlining-mode"),
-flagpd1("analyzer-list-enabled-checkers"),
-sepd1("analyzer-max-loop"),
-flagpd1("analyzer-opt-analyze-headers"),
-flagpd1("analyzer-opt-analyze-nested-blocks"),
-sepd1("analyzer-output"),
-sepd1("analyzer-purge"),
-flagpd1("analyzer-stats"),
-sepd1("analyzer-store"),
-flagpd1("analyzer-viz-egraph-graphviz"),
-flagpd1("analyzer-werror"),
-flagpd1("fslp-vectorize-aggressive"),
-flagpd1("fno-slp-vectorize-aggressive"),
-flagpd1("fexpensive-optimizations"),
-flagpd1("fno-expensive-optimizations"),
-flagpd1("fdefer-pop"),
-flagpd1("fno-defer-pop"),
-flagpd1("fextended-identifiers"),
-flagpd1("fno-extended-identifiers"),
-flagpd1("fhonor-infinites"),
-flagpd1("fno-honor-infinites"),
-flagpd1("findirect-virtual-calls"),
-sepd1("fnew-alignment"),
-flagpd1("faligned-new"),
-flagpd1("fno-aligned-new"),
-flagpd1("fsched-interblock"),
-flagpd1("ftree-vectorize"),
-flagpd1("fno-tree-vectorize"),
-flagpd1("ftree-slp-vectorize"),
-flagpd1("fno-tree-slp-vectorize"),
-flagpd1("fterminated-vtables"),
-flagpd1("grecord-gcc-switches"),
-flagpd1("gno-record-gcc-switches"),
-flagpd1("fident"),
-flagpd1("nocudalib"),
-.{
- .name = "system-header-prefix",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "no-system-header-prefix",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("integrated-as"),
-flagpd1("no-integrated-as"),
-flagpd1("fkeep-inline-functions"),
-flagpd1("fno-keep-inline-functions"),
-flagpd1("fno-semantic-interposition"),
-.{
- .name = "Gs",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "O1",
- .syntax = .flag,
- .zig_equivalent = .optimize,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "O2",
- .syntax = .flag,
- .zig_equivalent = .optimize,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-flagpd1("fno-ident"),
-.{
- .name = "Ob0",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Ob1",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Ob2",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Od",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Og",
- .syntax = .flag,
- .zig_equivalent = .optimize,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Oi",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Oi-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Os",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Ot",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Ox",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-flagpd1("fcuda-rdc"),
-.{
- .name = "Oy",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Oy-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-flagpd1("fno-cuda-rdc"),
-flagpd1("shared-libasan"),
-flagpd1("frecord-gcc-switches"),
-flagpd1("fno-record-gcc-switches"),
-.{
- .name = "ansi",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-sepd1("arch"),
-flagpd1("arch_errors_fatal"),
-sepd1("arch_only"),
-flagpd1("arcmt-check"),
-flagpd1("arcmt-migrate"),
-flagpd1("arcmt-migrate-emit-errors"),
-sepd1("arcmt-migrate-report-output"),
-flagpd1("arcmt-modify"),
-flagpd1("ast-dump"),
-flagpd1("ast-dump-all"),
-sepd1("ast-dump-filter"),
-flagpd1("ast-dump-lookups"),
-flagpd1("ast-list"),
-sepd1("ast-merge"),
-flagpd1("ast-print"),
-flagpd1("ast-view"),
-flagpd1("fautomatic"),
-flagpd1("fno-automatic"),
-sepd1("aux-triple"),
-flagpd1("fbackslash"),
-flagpd1("fno-backslash"),
-flagpd1("fbacktrace"),
-flagpd1("fno-backtrace"),
-flagpd1("bind_at_load"),
-flagpd1("fbounds-check"),
-flagpd1("fno-bounds-check"),
-flagpd1("fbranch-count-reg"),
-flagpd1("fno-branch-count-reg"),
-flagpd1("building-pch-with-obj"),
-flagpd1("bundle"),
-sepd1("bundle_loader"),
-.{
- .name = "c",
- .syntax = .flag,
- .zig_equivalent = .c,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("fcaller-saves"),
-flagpd1("fno-caller-saves"),
-flagpd1("cc1"),
-flagpd1("cc1as"),
-flagpd1("ccc-arcmt-check"),
-sepd1("ccc-arcmt-migrate"),
-flagpd1("ccc-arcmt-modify"),
-sepd1("ccc-gcc-name"),
-sepd1("ccc-install-dir"),
-sepd1("ccc-objcmt-migrate"),
-flagpd1("ccc-print-bindings"),
-flagpd1("ccc-print-phases"),
-flagpd1("cfguard"),
-flagpd1("cfguard-no-checks"),
-sepd1("chain-include"),
-flagpd1("fcheck-array-temporaries"),
-flagpd1("fno-check-array-temporaries"),
-flagpd1("cl-denorms-are-zero"),
-flagpd1("cl-fast-relaxed-math"),
-flagpd1("cl-finite-math-only"),
-flagpd1("cl-fp32-correctly-rounded-divide-sqrt"),
-flagpd1("cl-kernel-arg-info"),
-flagpd1("cl-mad-enable"),
-flagpd1("cl-no-signed-zeros"),
-flagpd1("cl-opt-disable"),
-flagpd1("cl-single-precision-constant"),
-flagpd1("cl-strict-aliasing"),
-flagpd1("cl-uniform-work-group-size"),
-flagpd1("cl-unsafe-math-optimizations"),
-sepd1("code-completion-at"),
-flagpd1("code-completion-brief-comments"),
-flagpd1("code-completion-macros"),
-flagpd1("code-completion-patterns"),
-flagpd1("code-completion-with-fixits"),
-.{
- .name = "combine",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("compiler-options-dump"),
-.{
- .name = "compress-debug-sections",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "config",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "coverage",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("coverage-cfg-checksum"),
-sepd1("coverage-data-file"),
-flagpd1("coverage-exit-block-before-body"),
-flagpd1("coverage-no-function-names-in-data"),
-sepd1("coverage-notes-file"),
-flagpd1("cpp"),
-flagpd1("cpp-precomp"),
-flagpd1("fcray-pointer"),
-flagpd1("fno-cray-pointer"),
-.{
- .name = "cuda-compile-host-device",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "cuda-device-only",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "cuda-host-only",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "cuda-noopt-device-debug",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "cuda-path-ignore-env",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("dA"),
-flagpd1("dD"),
-flagpd1("dI"),
-flagpd1("dM"),
-flagpd1("d"),
-flagpd1("fd-lines-as-code"),
-flagpd1("fno-d-lines-as-code"),
-flagpd1("fd-lines-as-comments"),
-flagpd1("fno-d-lines-as-comments"),
-flagpd1("dead_strip"),
-flagpd1("debug-forward-template-params"),
-flagpd1("debug-info-macro"),
-flagpd1("fdefault-double-8"),
-flagpd1("fno-default-double-8"),
-sepd1("default-function-attr"),
-flagpd1("fdefault-inline"),
-flagpd1("fno-default-inline"),
-flagpd1("fdefault-integer-8"),
-flagpd1("fno-default-integer-8"),
-flagpd1("fdefault-real-8"),
-flagpd1("fno-default-real-8"),
-sepd1("defsym"),
-sepd1("dependency-dot"),
-sepd1("dependency-file"),
-flagpd1("detailed-preprocessing-record"),
-flagpd1("fdevirtualize"),
-flagpd1("fno-devirtualize"),
-flagpd1("fdevirtualize-speculatively"),
-flagpd1("fno-devirtualize-speculatively"),
-sepd1("diagnostic-log-file"),
-sepd1("serialize-diagnostic-file"),
-flagpd1("disable-O0-optnone"),
-flagpd1("disable-free"),
-flagpd1("disable-lifetime-markers"),
-flagpd1("disable-llvm-optzns"),
-flagpd1("disable-llvm-passes"),
-flagpd1("disable-llvm-verifier"),
-flagpd1("disable-objc-default-synthesize-properties"),
-flagpd1("disable-pragma-debug-crash"),
-flagpd1("disable-red-zone"),
-flagpd1("discard-value-names"),
-flagpd1("fdollar-ok"),
-flagpd1("fno-dollar-ok"),
-flagpd1("dump-coverage-mapping"),
-flagpd1("dump-deserialized-decls"),
-flagpd1("fdump-fortran-optimized"),
-flagpd1("fno-dump-fortran-optimized"),
-flagpd1("fdump-fortran-original"),
-flagpd1("fno-dump-fortran-original"),
-flagpd1("fdump-parse-tree"),
-flagpd1("fno-dump-parse-tree"),
-flagpd1("dump-raw-tokens"),
-flagpd1("dump-tokens"),
-flagpd1("dumpmachine"),
-flagpd1("dumpspecs"),
-flagpd1("dumpversion"),
-flagpd1("dwarf-column-info"),
-sepd1("dwarf-debug-flags"),
-sepd1("dwarf-debug-producer"),
-flagpd1("dwarf-explicit-import"),
-flagpd1("dwarf-ext-refs"),
-sepd1("dylib_file"),
-flagpd1("dylinker"),
-flagpd1("dynamic"),
-flagpd1("dynamiclib"),
-flagpd1("feliminate-unused-debug-types"),
-flagpd1("fno-eliminate-unused-debug-types"),
-flagpd1("emit-ast"),
-flagpd1("emit-codegen-only"),
-flagpd1("emit-header-module"),
-flagpd1("emit-html"),
-flagpd1("emit-interface-stubs"),
-flagpd1("emit-llvm"),
-flagpd1("emit-llvm-bc"),
-flagpd1("emit-llvm-only"),
-flagpd1("emit-llvm-uselists"),
-flagpd1("emit-merged-ifs"),
-flagpd1("emit-module"),
-flagpd1("emit-module-interface"),
-flagpd1("emit-obj"),
-flagpd1("emit-pch"),
-flagpd1("enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang"),
-sepd1("error-on-deserialized-decl"),
-sepd1("exported_symbols_list"),
-flagpd1("fexternal-blas"),
-flagpd1("fno-external-blas"),
-flagpd1("ff2c"),
-flagpd1("fno-f2c"),
-.{
- .name = "fPIC",
- .syntax = .flag,
- .zig_equivalent = .pic,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("fPIE"),
-flagpd1("faccess-control"),
-flagpd1("faddrsig"),
-flagpd1("falign-functions"),
-flagpd1("faligned-allocation"),
-flagpd1("fallow-editor-placeholders"),
-flagpd1("fallow-half-arguments-and-returns"),
-flagpd1("fallow-pch-with-compiler-errors"),
-flagpd1("fallow-unsupported"),
-flagpd1("faltivec"),
-flagpd1("fansi-escape-codes"),
-flagpd1("fapple-kext"),
-flagpd1("fapple-link-rtlib"),
-flagpd1("fapple-pragma-pack"),
-flagpd1("fapplication-extension"),
-flagpd1("fapply-global-visibility-to-externs"),
-flagpd1("fasm"),
-flagpd1("fasm-blocks"),
-flagpd1("fassociative-math"),
-flagpd1("fassume-sane-operator-new"),
-flagpd1("fast"),
-flagpd1("fastcp"),
-flagpd1("fastf"),
-flagpd1("fasynchronous-unwind-tables"),
-flagpd1("ffat-lto-objects"),
-flagpd1("fno-fat-lto-objects"),
-flagpd1("fauto-profile"),
-flagpd1("fauto-profile-accurate"),
-flagpd1("fautolink"),
-flagpd1("fblocks"),
-flagpd1("fblocks-runtime-optional"),
-flagpd1("fborland-extensions"),
-sepd1("fbracket-depth"),
-flagpd1("fbuiltin"),
-flagpd1("fbuiltin-module-map"),
-flagpd1("fcall-saved-x10"),
-flagpd1("fcall-saved-x11"),
-flagpd1("fcall-saved-x12"),
-flagpd1("fcall-saved-x13"),
-flagpd1("fcall-saved-x14"),
-flagpd1("fcall-saved-x15"),
-flagpd1("fcall-saved-x18"),
-flagpd1("fcall-saved-x8"),
-flagpd1("fcall-saved-x9"),
-flagpd1("fcaret-diagnostics"),
-sepd1("fcaret-diagnostics-max-lines"),
-flagpd1("fcf-protection"),
-flagpd1("fchar8_t"),
-flagpd1("fcheck-new"),
-flagpd1("fno-check-new"),
-flagpd1("fcolor-diagnostics"),
-flagpd1("fcommon"),
-flagpd1("fcomplete-member-pointers"),
-flagpd1("fconcepts-ts"),
-flagpd1("fconst-strings"),
-flagpd1("fconstant-cfstrings"),
-sepd1("fconstant-string-class"),
-sepd1("fconstexpr-backtrace-limit"),
-sepd1("fconstexpr-depth"),
-sepd1("fconstexpr-steps"),
-flagpd1("fconvergent-functions"),
-flagpd1("fcoroutines-ts"),
-flagpd1("fcoverage-mapping"),
-flagpd1("fcreate-profile"),
-flagpd1("fcs-profile-generate"),
-flagpd1("fcuda-allow-variadic-functions"),
-flagpd1("fcuda-approx-transcendentals"),
-flagpd1("fcuda-flush-denormals-to-zero"),
-sepd1("fcuda-include-gpubinary"),
-flagpd1("fcuda-is-device"),
-flagpd1("fcuda-short-ptr"),
-flagpd1("fcxx-exceptions"),
-flagpd1("fcxx-modules"),
-flagpd1("fc++-static-destructors"),
-flagpd1("fdata-sections"),
-sepd1("fdebug-compilation-dir"),
-flagpd1("fdebug-info-for-profiling"),
-flagpd1("fdebug-macro"),
-flagpd1("fdebug-pass-arguments"),
-flagpd1("fdebug-pass-manager"),
-flagpd1("fdebug-pass-structure"),
-flagpd1("fdebug-ranges-base-address"),
-flagpd1("fdebug-types-section"),
-flagpd1("fdebugger-cast-result-to-id"),
-flagpd1("fdebugger-objc-literal"),
-flagpd1("fdebugger-support"),
-flagpd1("fdeclare-opencl-builtins"),
-flagpd1("fdeclspec"),
-flagpd1("fdelayed-template-parsing"),
-flagpd1("fdelete-null-pointer-checks"),
-flagpd1("fdeprecated-macro"),
-flagpd1("fdiagnostics-absolute-paths"),
-flagpd1("fdiagnostics-color"),
-flagpd1("fdiagnostics-fixit-info"),
-sepd1("fdiagnostics-format"),
-flagpd1("fdiagnostics-parseable-fixits"),
-flagpd1("fdiagnostics-print-source-range-info"),
-sepd1("fdiagnostics-show-category"),
-flagpd1("fdiagnostics-show-hotness"),
-flagpd1("fdiagnostics-show-note-include-stack"),
-flagpd1("fdiagnostics-show-option"),
-flagpd1("fdiagnostics-show-template-tree"),
-flagpd1("fdigraphs"),
-flagpd1("fdisable-module-hash"),
-flagpd1("fdiscard-value-names"),
-flagpd1("fdollars-in-identifiers"),
-flagpd1("fdouble-square-bracket-attributes"),
-flagpd1("fdump-record-layouts"),
-flagpd1("fdump-record-layouts-simple"),
-flagpd1("fdump-vtable-layouts"),
-flagpd1("fdwarf2-cfi-asm"),
-flagpd1("fdwarf-directory-asm"),
-flagpd1("fdwarf-exceptions"),
-flagpd1("felide-constructors"),
-flagpd1("feliminate-unused-debug-symbols"),
-flagpd1("fembed-bitcode"),
-flagpd1("fembed-bitcode-marker"),
-flagpd1("femit-all-decls"),
-flagpd1("femit-coverage-data"),
-flagpd1("femit-coverage-notes"),
-flagpd1("femit-debug-entry-values"),
-flagpd1("femulated-tls"),
-flagpd1("fencode-extended-block-signature"),
-sepd1("ferror-limit"),
-flagpd1("fescaping-block-tail-calls"),
-flagpd1("fexceptions"),
-flagpd1("fexperimental-isel"),
-flagpd1("fexperimental-new-constant-interpreter"),
-flagpd1("fexperimental-new-pass-manager"),
-flagpd1("fexternc-nounwind"),
-flagpd1("ffake-address-space-map"),
-flagpd1("ffast-math"),
-flagpd1("ffine-grained-bitfield-accesses"),
-flagpd1("ffinite-math-only"),
-flagpd1("ffixed-point"),
-flagpd1("ffixed-r19"),
-flagpd1("ffixed-r9"),
-flagpd1("ffixed-x1"),
-flagpd1("ffixed-x10"),
-flagpd1("ffixed-x11"),
-flagpd1("ffixed-x12"),
-flagpd1("ffixed-x13"),
-flagpd1("ffixed-x14"),
-flagpd1("ffixed-x15"),
-flagpd1("ffixed-x16"),
-flagpd1("ffixed-x17"),
-flagpd1("ffixed-x18"),
-flagpd1("ffixed-x19"),
-flagpd1("ffixed-x2"),
-flagpd1("ffixed-x20"),
-flagpd1("ffixed-x21"),
-flagpd1("ffixed-x22"),
-flagpd1("ffixed-x23"),
-flagpd1("ffixed-x24"),
-flagpd1("ffixed-x25"),
-flagpd1("ffixed-x26"),
-flagpd1("ffixed-x27"),
-flagpd1("ffixed-x28"),
-flagpd1("ffixed-x29"),
-flagpd1("ffixed-x3"),
-flagpd1("ffixed-x30"),
-flagpd1("ffixed-x31"),
-flagpd1("ffixed-x4"),
-flagpd1("ffixed-x5"),
-flagpd1("ffixed-x6"),
-flagpd1("ffixed-x7"),
-flagpd1("ffixed-x8"),
-flagpd1("ffixed-x9"),
-flagpd1("ffor-scope"),
-flagpd1("fforbid-guard-variables"),
-flagpd1("fforce-dwarf-frame"),
-flagpd1("fforce-emit-vtables"),
-flagpd1("fforce-enable-int128"),
-flagpd1("ffreestanding"),
-flagpd1("ffunction-sections"),
-flagpd1("fgnu89-inline"),
-flagpd1("fgnu-inline-asm"),
-flagpd1("fgnu-keywords"),
-flagpd1("fgnu-runtime"),
-flagpd1("fgpu-allow-device-init"),
-flagpd1("fgpu-rdc"),
-flagpd1("fheinous-gnu-extensions"),
-flagpd1("fhip-dump-offload-linker-script"),
-flagpd1("fhip-new-launch-api"),
-flagpd1("fhonor-infinities"),
-flagpd1("fhonor-nans"),
-flagpd1("fhosted"),
-sepd1("filelist"),
-sepd1("filetype"),
-flagpd1("fimplicit-module-maps"),
-flagpd1("fimplicit-modules"),
-flagpd1("finclude-default-header"),
-flagpd1("finline"),
-flagpd1("finline-functions"),
-flagpd1("finline-hint-functions"),
-flagpd1("finline-limit"),
-flagpd1("fno-inline-limit"),
-flagpd1("finstrument-function-entry-bare"),
-flagpd1("finstrument-functions"),
-flagpd1("finstrument-functions-after-inlining"),
-flagpd1("fintegrated-as"),
-flagpd1("fintegrated-cc1"),
-flagpd1("fix-only-warnings"),
-flagpd1("fix-what-you-can"),
-flagpd1("ffixed-form"),
-flagpd1("fno-fixed-form"),
-flagpd1("fixit"),
-flagpd1("fixit-recompile"),
-flagpd1("fixit-to-temporary"),
-flagpd1("fjump-tables"),
-flagpd1("fkeep-static-consts"),
-flagpd1("flat_namespace"),
-flagpd1("flax-vector-conversions"),
-flagpd1("flimit-debug-info"),
-flagpd1("ffloat-store"),
-flagpd1("fno-float-store"),
-flagpd1("flto"),
-flagpd1("flto-unit"),
-flagpd1("flto-visibility-public-std"),
-sepd1("fmacro-backtrace-limit"),
-flagpd1("fmath-errno"),
-flagpd1("fmerge-all-constants"),
-flagpd1("fmerge-functions"),
-sepd1("fmessage-length"),
-sepd1("fmodule-feature"),
-flagpd1("fmodule-file-deps"),
-sepd1("fmodule-implementation-of"),
-flagpd1("fmodule-map-file-home-is-cwd"),
-flagpd1("fmodule-maps"),
-sepd1("fmodule-name"),
-flagpd1("fmodules"),
-flagpd1("fmodules-codegen"),
-flagpd1("fmodules-debuginfo"),
-flagpd1("fmodules-decluse"),
-flagpd1("fmodules-disable-diagnostic-validation"),
-flagpd1("fmodules-hash-content"),
-flagpd1("fmodules-local-submodule-visibility"),
-flagpd1("fmodules-search-all"),
-flagpd1("fmodules-strict-context-hash"),
-flagpd1("fmodules-strict-decluse"),
-flagpd1("fmodules-ts"),
-sepd1("fmodules-user-build-path"),
-flagpd1("fmodules-validate-input-files-content"),
-flagpd1("fmodules-validate-once-per-build-session"),
-flagpd1("fmodules-validate-system-headers"),
-flagpd1("fms-compatibility"),
-flagpd1("fms-extensions"),
-flagpd1("fms-volatile"),
-flagpd1("fmudflap"),
-flagpd1("fmudflapth"),
-flagpd1("fnative-half-arguments-and-returns"),
-flagpd1("fnative-half-type"),
-flagpd1("fnested-functions"),
-flagpd1("fnext-runtime"),
-.{
- .name = "fno-PIC",
- .syntax = .flag,
- .zig_equivalent = .no_pic,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("fno-PIE"),
-flagpd1("fno-access-control"),
-flagpd1("fno-addrsig"),
-flagpd1("fno-align-functions"),
-flagpd1("fno-aligned-allocation"),
-flagpd1("fno-allow-editor-placeholders"),
-flagpd1("fno-altivec"),
-flagpd1("fno-apple-pragma-pack"),
-flagpd1("fno-application-extension"),
-flagpd1("fno-asm"),
-flagpd1("fno-asm-blocks"),
-flagpd1("fno-associative-math"),
-flagpd1("fno-assume-sane-operator-new"),
-flagpd1("fno-asynchronous-unwind-tables"),
-flagpd1("fno-auto-profile"),
-flagpd1("fno-auto-profile-accurate"),
-flagpd1("fno-autolink"),
-flagpd1("fno-bitfield-type-align"),
-flagpd1("fno-blocks"),
-flagpd1("fno-borland-extensions"),
-flagpd1("fno-builtin"),
-flagpd1("fno-caret-diagnostics"),
-flagpd1("fno-char8_t"),
-flagpd1("fno-color-diagnostics"),
-flagpd1("fno-common"),
-flagpd1("fno-complete-member-pointers"),
-flagpd1("fno-concept-satisfaction-caching"),
-flagpd1("fno-const-strings"),
-flagpd1("fno-constant-cfstrings"),
-flagpd1("fno-coroutines-ts"),
-flagpd1("fno-coverage-mapping"),
-flagpd1("fno-crash-diagnostics"),
-flagpd1("fno-cuda-approx-transcendentals"),
-flagpd1("fno-cuda-flush-denormals-to-zero"),
-flagpd1("fno-cuda-host-device-constexpr"),
-flagpd1("fno-cuda-short-ptr"),
-flagpd1("fno-cxx-exceptions"),
-flagpd1("fno-cxx-modules"),
-flagpd1("fno-c++-static-destructors"),
-flagpd1("fno-data-sections"),
-flagpd1("fno-debug-info-for-profiling"),
-flagpd1("fno-debug-macro"),
-flagpd1("fno-debug-pass-manager"),
-flagpd1("fno-debug-ranges-base-address"),
-flagpd1("fno-debug-types-section"),
-flagpd1("fno-declspec"),
-flagpd1("fno-delayed-template-parsing"),
-flagpd1("fno-delete-null-pointer-checks"),
-flagpd1("fno-deprecated-macro"),
-flagpd1("fno-diagnostics-color"),
-flagpd1("fno-diagnostics-fixit-info"),
-flagpd1("fno-diagnostics-show-hotness"),
-flagpd1("fno-diagnostics-show-note-include-stack"),
-flagpd1("fno-diagnostics-show-option"),
-flagpd1("fno-diagnostics-use-presumed-location"),
-flagpd1("fno-digraphs"),
-flagpd1("fno-discard-value-names"),
-flagpd1("fno-dllexport-inlines"),
-flagpd1("fno-dollars-in-identifiers"),
-flagpd1("fno-double-square-bracket-attributes"),
-flagpd1("fno-dwarf2-cfi-asm"),
-flagpd1("fno-dwarf-directory-asm"),
-flagpd1("fno-elide-constructors"),
-flagpd1("fno-elide-type"),
-flagpd1("fno-eliminate-unused-debug-symbols"),
-flagpd1("fno-emulated-tls"),
-flagpd1("fno-escaping-block-tail-calls"),
-flagpd1("fno-exceptions"),
-flagpd1("fno-experimental-isel"),
-flagpd1("fno-experimental-new-pass-manager"),
-flagpd1("fno-fast-math"),
-flagpd1("fno-fine-grained-bitfield-accesses"),
-flagpd1("fno-finite-math-only"),
-flagpd1("fno-fixed-point"),
-flagpd1("fno-for-scope"),
-flagpd1("fno-force-dwarf-frame"),
-flagpd1("fno-force-emit-vtables"),
-flagpd1("fno-force-enable-int128"),
-flagpd1("fno-function-sections"),
-flagpd1("fno-gnu89-inline"),
-flagpd1("fno-gnu-inline-asm"),
-flagpd1("fno-gnu-keywords"),
-flagpd1("fno-gpu-allow-device-init"),
-flagpd1("fno-gpu-rdc"),
-flagpd1("fno-hip-new-launch-api"),
-flagpd1("fno-honor-infinities"),
-flagpd1("fno-honor-nans"),
-flagpd1("fno-implicit-module-maps"),
-flagpd1("fno-implicit-modules"),
-flagpd1("fno-inline"),
-flagpd1("fno-inline-functions"),
-flagpd1("fno-integrated-as"),
-flagpd1("fno-integrated-cc1"),
-flagpd1("fno-jump-tables"),
-flagpd1("fno-lax-vector-conversions"),
-flagpd1("fno-limit-debug-info"),
-flagpd1("fno-lto"),
-flagpd1("fno-lto-unit"),
-flagpd1("fno-math-builtin"),
-flagpd1("fno-math-errno"),
-flagpd1("fno-max-type-align"),
-flagpd1("fno-merge-all-constants"),
-flagpd1("fno-module-file-deps"),
-flagpd1("fno-module-maps"),
-flagpd1("fno-modules"),
-flagpd1("fno-modules-decluse"),
-flagpd1("fno-modules-error-recovery"),
-flagpd1("fno-modules-global-index"),
-flagpd1("fno-modules-search-all"),
-flagpd1("fno-strict-modules-decluse"),
-flagpd1("fno_modules-validate-input-files-content"),
-flagpd1("fno-modules-validate-system-headers"),
-flagpd1("fno-ms-compatibility"),
-flagpd1("fno-ms-extensions"),
-flagpd1("fno-objc-arc"),
-flagpd1("fno-objc-arc-exceptions"),
-flagpd1("fno-objc-convert-messages-to-runtime-calls"),
-flagpd1("fno-objc-exceptions"),
-flagpd1("fno-objc-infer-related-result-type"),
-flagpd1("fno-objc-legacy-dispatch"),
-flagpd1("fno-objc-nonfragile-abi"),
-flagpd1("fno-objc-weak"),
-flagpd1("fno-omit-frame-pointer"),
-flagpd1("fno-openmp"),
-flagpd1("fno-openmp-cuda-force-full-runtime"),
-flagpd1("fno-openmp-cuda-mode"),
-flagpd1("fno-openmp-optimistic-collapse"),
-flagpd1("fno-openmp-simd"),
-flagpd1("fno-operator-names"),
-flagpd1("fno-optimize-sibling-calls"),
-flagpd1("fno-pack-struct"),
-flagpd1("fno-padding-on-unsigned-fixed-point"),
-flagpd1("fno-pascal-strings"),
-flagpd1("fno-pch-timestamp"),
-flagpd1("fno_pch-validate-input-files-content"),
-flagpd1("fno-pic"),
-flagpd1("fno-pie"),
-flagpd1("fno-plt"),
-flagpd1("fno-preserve-as-comments"),
-flagpd1("fno-profile-arcs"),
-flagpd1("fno-profile-generate"),
-flagpd1("fno-profile-instr-generate"),
-flagpd1("fno-profile-instr-use"),
-flagpd1("fno-profile-sample-accurate"),
-flagpd1("fno-profile-sample-use"),
-flagpd1("fno-profile-use"),
-flagpd1("fno-reciprocal-math"),
-flagpd1("fno-record-command-line"),
-flagpd1("fno-register-global-dtors-with-atexit"),
-flagpd1("fno-relaxed-template-template-args"),
-flagpd1("fno-reroll-loops"),
-flagpd1("fno-rewrite-imports"),
-flagpd1("fno-rewrite-includes"),
-flagpd1("fno-ropi"),
-flagpd1("fno-rounding-math"),
-flagpd1("fno-rtlib-add-rpath"),
-flagpd1("fno-rtti"),
-flagpd1("fno-rtti-data"),
-flagpd1("fno-rwpi"),
-flagpd1("fno-sanitize-address-poison-custom-array-cookie"),
-flagpd1("fno-sanitize-address-use-after-scope"),
-flagpd1("fno-sanitize-address-use-odr-indicator"),
-flagpd1("fno-sanitize-blacklist"),
-flagpd1("fno-sanitize-cfi-canonical-jump-tables"),
-flagpd1("fno-sanitize-cfi-cross-dso"),
-flagpd1("fno-sanitize-link-c++-runtime"),
-flagpd1("fno-sanitize-link-runtime"),
-flagpd1("fno-sanitize-memory-track-origins"),
-flagpd1("fno-sanitize-memory-use-after-dtor"),
-flagpd1("fno-sanitize-minimal-runtime"),
-flagpd1("fno-sanitize-recover"),
-flagpd1("fno-sanitize-stats"),
-flagpd1("fno-sanitize-thread-atomics"),
-flagpd1("fno-sanitize-thread-func-entry-exit"),
-flagpd1("fno-sanitize-thread-memory-access"),
-flagpd1("fno-sanitize-undefined-trap-on-error"),
-flagpd1("fno-save-optimization-record"),
-flagpd1("fno-short-enums"),
-flagpd1("fno-short-wchar"),
-flagpd1("fno-show-column"),
-flagpd1("fno-show-source-location"),
-flagpd1("fno-signaling-math"),
-flagpd1("fno-signed-char"),
-flagpd1("fno-signed-wchar"),
-flagpd1("fno-signed-zeros"),
-flagpd1("fno-sized-deallocation"),
-flagpd1("fno-slp-vectorize"),
-flagpd1("fno-spell-checking"),
-flagpd1("fno-split-dwarf-inlining"),
-flagpd1("fno-split-lto-unit"),
-flagpd1("fno-stack-protector"),
-flagpd1("fno-stack-size-section"),
-flagpd1("fno-standalone-debug"),
-flagpd1("fno-strict-aliasing"),
-flagpd1("fno-strict-enums"),
-flagpd1("fno-strict-float-cast-overflow"),
-flagpd1("fno-strict-overflow"),
-flagpd1("fno-strict-return"),
-flagpd1("fno-strict-vtable-pointers"),
-flagpd1("fno-struct-path-tbaa"),
-flagpd1("fno-temp-file"),
-flagpd1("fno-threadsafe-statics"),
-flagpd1("fno-trapping-math"),
-flagpd1("fno-trigraphs"),
-flagpd1("fno-unique-section-names"),
-flagpd1("fno-unit-at-a-time"),
-flagpd1("fno-unroll-loops"),
-flagpd1("fno-unsafe-math-optimizations"),
-flagpd1("fno-unsigned-char"),
-flagpd1("fno-unwind-tables"),
-flagpd1("fno-use-cxa-atexit"),
-flagpd1("fno-use-init-array"),
-flagpd1("fno-use-line-directives"),
-flagpd1("fno-validate-pch"),
-flagpd1("fno-var-tracking"),
-flagpd1("fno-vectorize"),
-flagpd1("fno-verbose-asm"),
-flagpd1("fno-virtual-function_elimination"),
-flagpd1("fno-wchar"),
-flagpd1("fno-whole-program-vtables"),
-flagpd1("fno-working-directory"),
-flagpd1("fno-wrapv"),
-flagpd1("fno-zero-initialized-in-bss"),
-flagpd1("fno-zvector"),
-flagpd1("fnoopenmp-relocatable-target"),
-flagpd1("fnoopenmp-use-tls"),
-flagpd1("fno-xray-always-emit-customevents"),
-flagpd1("fno-xray-always-emit-typedevents"),
-flagpd1("fno-xray-instrument"),
-flagpd1("fnoxray-link-deps"),
-flagpd1("fobjc-arc"),
-flagpd1("fobjc-arc-exceptions"),
-flagpd1("fobjc-atdefs"),
-flagpd1("fobjc-call-cxx-cdtors"),
-flagpd1("fobjc-convert-messages-to-runtime-calls"),
-flagpd1("fobjc-exceptions"),
-flagpd1("fobjc-gc"),
-flagpd1("fobjc-gc-only"),
-flagpd1("fobjc-infer-related-result-type"),
-flagpd1("fobjc-legacy-dispatch"),
-flagpd1("fobjc-link-runtime"),
-flagpd1("fobjc-new-property"),
-flagpd1("fobjc-nonfragile-abi"),
-flagpd1("fobjc-runtime-has-weak"),
-flagpd1("fobjc-sender-dependent-dispatch"),
-flagpd1("fobjc-subscripting-legacy-runtime"),
-flagpd1("fobjc-weak"),
-flagpd1("fomit-frame-pointer"),
-flagpd1("fopenmp"),
-flagpd1("fopenmp-cuda-force-full-runtime"),
-flagpd1("fopenmp-cuda-mode"),
-flagpd1("fopenmp-enable-irbuilder"),
-sepd1("fopenmp-host-ir-file-path"),
-flagpd1("fopenmp-is-device"),
-flagpd1("fopenmp-optimistic-collapse"),
-flagpd1("fopenmp-relocatable-target"),
-flagpd1("fopenmp-simd"),
-flagpd1("fopenmp-use-tls"),
-sepd1("foperator-arrow-depth"),
-flagpd1("foptimize-sibling-calls"),
-flagpd1("force_cpusubtype_ALL"),
-flagpd1("force_flat_namespace"),
-sepd1("force_load"),
-flagpd1("forder-file-instrumentation"),
-flagpd1("fpack-struct"),
-flagpd1("fpadding-on-unsigned-fixed-point"),
-flagpd1("fparse-all-comments"),
-flagpd1("fpascal-strings"),
-flagpd1("fpcc-struct-return"),
-flagpd1("fpch-preprocess"),
-flagpd1("fpch-validate-input-files-content"),
-flagpd1("fpic"),
-flagpd1("fpie"),
-flagpd1("fplt"),
-flagpd1("fpreserve-as-comments"),
-flagpd1("fpreserve-vec3-type"),
-flagpd1("fprofile-arcs"),
-flagpd1("fprofile-generate"),
-flagpd1("fprofile-instr-generate"),
-flagpd1("fprofile-instr-use"),
-sepd1("fprofile-remapping-file"),
-flagpd1("fprofile-sample-accurate"),
-flagpd1("fprofile-sample-use"),
-flagpd1("fprofile-use"),
-.{
- .name = "framework",
- .syntax = .separate,
- .zig_equivalent = .framework,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("freciprocal-math"),
-flagpd1("frecord-command-line"),
-flagpd1("ffree-form"),
-flagpd1("fno-free-form"),
-flagpd1("freg-struct-return"),
-flagpd1("fregister-global-dtors-with-atexit"),
-flagpd1("frelaxed-template-template-args"),
-flagpd1("freroll-loops"),
-flagpd1("fretain-comments-from-system-headers"),
-flagpd1("frewrite-imports"),
-flagpd1("frewrite-includes"),
-sepd1("frewrite-map-file"),
-flagpd1("ffriend-injection"),
-flagpd1("fno-friend-injection"),
-flagpd1("ffrontend-optimize"),
-flagpd1("fno-frontend-optimize"),
-flagpd1("fropi"),
-flagpd1("frounding-math"),
-flagpd1("frtlib-add-rpath"),
-flagpd1("frtti"),
-flagpd1("frwpi"),
-flagpd1("fsanitize-address-globals-dead-stripping"),
-flagpd1("fsanitize-address-poison-custom-array-cookie"),
-flagpd1("fsanitize-address-use-after-scope"),
-flagpd1("fsanitize-address-use-odr-indicator"),
-flagpd1("fsanitize-cfi-canonical-jump-tables"),
-flagpd1("fsanitize-cfi-cross-dso"),
-flagpd1("fsanitize-cfi-icall-generalize-pointers"),
-flagpd1("fsanitize-coverage-8bit-counters"),
-flagpd1("fsanitize-coverage-indirect-calls"),
-flagpd1("fsanitize-coverage-inline-8bit-counters"),
-flagpd1("fsanitize-coverage-no-prune"),
-flagpd1("fsanitize-coverage-pc-table"),
-flagpd1("fsanitize-coverage-stack-depth"),
-flagpd1("fsanitize-coverage-trace-bb"),
-flagpd1("fsanitize-coverage-trace-cmp"),
-flagpd1("fsanitize-coverage-trace-div"),
-flagpd1("fsanitize-coverage-trace-gep"),
-flagpd1("fsanitize-coverage-trace-pc"),
-flagpd1("fsanitize-coverage-trace-pc-guard"),
-flagpd1("fsanitize-link-c++-runtime"),
-flagpd1("fsanitize-link-runtime"),
-flagpd1("fsanitize-memory-track-origins"),
-flagpd1("fsanitize-memory-use-after-dtor"),
-flagpd1("fsanitize-minimal-runtime"),
-flagpd1("fsanitize-recover"),
-flagpd1("fsanitize-stats"),
-flagpd1("fsanitize-thread-atomics"),
-flagpd1("fsanitize-thread-func-entry-exit"),
-flagpd1("fsanitize-thread-memory-access"),
-flagpd1("fsanitize-undefined-trap-on-error"),
-flagpd1("fsave-optimization-record"),
-flagpd1("fseh-exceptions"),
-flagpd1("fshort-enums"),
-flagpd1("fshort-wchar"),
-flagpd1("fshow-column"),
-flagpd1("fshow-source-location"),
-flagpd1("fsignaling-math"),
-flagpd1("fsigned-bitfields"),
-flagpd1("fsigned-char"),
-flagpd1("fsigned-wchar"),
-flagpd1("fsigned-zeros"),
-flagpd1("fsized-deallocation"),
-flagpd1("fsjlj-exceptions"),
-flagpd1("fslp-vectorize"),
-flagpd1("fspell-checking"),
-sepd1("fspell-checking-limit"),
-flagpd1("fsplit-dwarf-inlining"),
-flagpd1("fsplit-lto-unit"),
-flagpd1("fsplit-stack"),
-flagpd1("fstack-protector"),
-flagpd1("fstack-protector-all"),
-flagpd1("fstack-protector-strong"),
-flagpd1("fstack-size-section"),
-flagpd1("fstandalone-debug"),
-flagpd1("fstrict-aliasing"),
-flagpd1("fstrict-enums"),
-flagpd1("fstrict-float-cast-overflow"),
-flagpd1("fstrict-overflow"),
-flagpd1("fstrict-return"),
-flagpd1("fstrict-vtable-pointers"),
-flagpd1("fstruct-path-tbaa"),
-flagpd1("fsycl-is-device"),
-flagpd1("fsyntax-only"),
-sepd1("ftabstop"),
-sepd1("ftemplate-backtrace-limit"),
-sepd1("ftemplate-depth"),
-flagpd1("ftest-coverage"),
-flagpd1("fthreadsafe-statics"),
-flagpd1("ftime-report"),
-flagpd1("ftime-trace"),
-flagpd1("ftrapping-math"),
-flagpd1("ftrapv"),
-sepd1("ftrapv-handler"),
-flagpd1("ftrigraphs"),
-sepd1("ftype-visibility"),
-sepd1("function-alignment"),
-flagpd1("ffunction-attribute-list"),
-flagpd1("fno-function-attribute-list"),
-flagpd1("funique-section-names"),
-flagpd1("funit-at-a-time"),
-flagpd1("funknown-anytype"),
-flagpd1("funroll-loops"),
-flagpd1("funsafe-math-optimizations"),
-flagpd1("funsigned-bitfields"),
-flagpd1("funsigned-char"),
-flagpd1("funwind-tables"),
-flagpd1("fuse-cxa-atexit"),
-flagpd1("fuse-init-array"),
-flagpd1("fuse-line-directives"),
-flagpd1("fuse-register-sized-bitfield-access"),
-flagpd1("fvalidate-ast-input-files-content"),
-flagpd1("fvectorize"),
-flagpd1("fverbose-asm"),
-flagpd1("fvirtual-function-elimination"),
-sepd1("fvisibility"),
-flagpd1("fvisibility-global-new-delete-hidden"),
-flagpd1("fvisibility-inlines-hidden"),
-flagpd1("fvisibility-ms-compat"),
-flagpd1("fwasm-exceptions"),
-flagpd1("fwhole-program-vtables"),
-flagpd1("fwrapv"),
-flagpd1("fwritable-strings"),
-flagpd1("fxray-always-emit-customevents"),
-flagpd1("fxray-always-emit-typedevents"),
-flagpd1("fxray-instrument"),
-flagpd1("fxray-link-deps"),
-flagpd1("fzero-initialized-in-bss"),
-flagpd1("fzvector"),
-flagpd1("g0"),
-flagpd1("g1"),
-flagpd1("g2"),
-flagpd1("g3"),
-.{
- .name = "g",
- .syntax = .flag,
- .zig_equivalent = .debug,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-sepd1("gcc-toolchain"),
-flagpd1("gcodeview"),
-flagpd1("gcodeview-ghash"),
-flagpd1("gcolumn-info"),
-flagpd1("fgcse-after-reload"),
-flagpd1("fno-gcse-after-reload"),
-flagpd1("fgcse"),
-flagpd1("fno-gcse"),
-flagpd1("fgcse-las"),
-flagpd1("fno-gcse-las"),
-flagpd1("fgcse-sm"),
-flagpd1("fno-gcse-sm"),
-flagpd1("gdwarf"),
-flagpd1("gdwarf-2"),
-flagpd1("gdwarf-3"),
-flagpd1("gdwarf-4"),
-flagpd1("gdwarf-5"),
-flagpd1("gdwarf-aranges"),
-flagpd1("gembed-source"),
-sepd1("gen-cdb-fragment-path"),
-flagpd1("gen-reproducer"),
-flagpd1("gfull"),
-flagpd1("ggdb"),
-flagpd1("ggdb0"),
-flagpd1("ggdb1"),
-flagpd1("ggdb2"),
-flagpd1("ggdb3"),
-flagpd1("ggnu-pubnames"),
-flagpd1("ginline-line-tables"),
-flagpd1("gline-directives-only"),
-flagpd1("gline-tables-only"),
-flagpd1("glldb"),
-flagpd1("gmlt"),
-flagpd1("gmodules"),
-flagpd1("gno-codeview-ghash"),
-flagpd1("gno-column-info"),
-flagpd1("gno-embed-source"),
-flagpd1("gno-gnu-pubnames"),
-flagpd1("gno-inline-line-tables"),
-flagpd1("gno-pubnames"),
-flagpd1("gno-record-command-line"),
-flagpd1("gno-strict-dwarf"),
-flagpd1("fgnu"),
-flagpd1("fno-gnu"),
-flagpd1("gpubnames"),
-flagpd1("grecord-command-line"),
-flagpd1("gsce"),
-flagpd1("gsplit-dwarf"),
-flagpd1("gstrict-dwarf"),
-flagpd1("gtoggle"),
-flagpd1("gused"),
-flagpd1("gz"),
-sepd1("header-include-file"),
-.{
- .name = "help",
- .syntax = .flag,
- .zig_equivalent = .driver_punt,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "hip-link",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-sepd1("image_base"),
-flagpd1("fimplement-inlines"),
-flagpd1("fno-implement-inlines"),
-flagpd1("fimplicit-none"),
-flagpd1("fno-implicit-none"),
-flagpd1("fimplicit-templates"),
-flagpd1("fno-implicit-templates"),
-sepd1("imultilib"),
-sepd1("include-pch"),
-flagpd1("index-header-map"),
-sepd1("init"),
-flagpd1("finit-local-zero"),
-flagpd1("fno-init-local-zero"),
-flagpd1("init-only"),
-flagpd1("finline-functions-called-once"),
-flagpd1("fno-inline-functions-called-once"),
-flagpd1("finline-small-functions"),
-flagpd1("fno-inline-small-functions"),
-sepd1("install_name"),
-flagpd1("finteger-4-integer-8"),
-flagpd1("fno-integer-4-integer-8"),
-flagpd1("fintrinsic-modules-path"),
-flagpd1("fno-intrinsic-modules-path"),
-flagpd1("fipa-cp"),
-flagpd1("fno-ipa-cp"),
-flagpd1("fivopts"),
-flagpd1("fno-ivopts"),
-flagpd1("keep_private_externs"),
-sepd1("lazy_framework"),
-sepd1("lazy_library"),
-sepd1("load"),
-flagpd1("m16"),
-flagpd1("m32"),
-flagpd1("m3dnow"),
-flagpd1("m3dnowa"),
-flagpd1("m64"),
-flagpd1("m80387"),
-flagpd1("mabi=ieeelongdouble"),
-flagpd1("mabicalls"),
-flagpd1("madx"),
-flagpd1("maes"),
-sepd1("main-file-name"),
-flagpd1("malign-double"),
-flagpd1("maltivec"),
-flagpd1("marm"),
-flagpd1("masm-verbose"),
-flagpd1("massembler-fatal-warnings"),
-flagpd1("massembler-no-warn"),
-flagpd1("matomics"),
-flagpd1("mavx"),
-flagpd1("mavx2"),
-flagpd1("mavx512bf16"),
-flagpd1("mavx512bitalg"),
-flagpd1("mavx512bw"),
-flagpd1("mavx512cd"),
-flagpd1("mavx512dq"),
-flagpd1("mavx512er"),
-flagpd1("mavx512f"),
-flagpd1("mavx512ifma"),
-flagpd1("mavx512pf"),
-flagpd1("mavx512vbmi"),
-flagpd1("mavx512vbmi2"),
-flagpd1("mavx512vl"),
-flagpd1("mavx512vnni"),
-flagpd1("mavx512vp2intersect"),
-flagpd1("mavx512vpopcntdq"),
-flagpd1("fmax-identifier-length"),
-flagpd1("fno-max-identifier-length"),
-flagpd1("mbackchain"),
-flagpd1("mbig-endian"),
-flagpd1("mbmi"),
-flagpd1("mbmi2"),
-flagpd1("mbranch-likely"),
-flagpd1("mbranch-target-enforce"),
-flagpd1("mbranches-within-32B-boundaries"),
-flagpd1("mbulk-memory"),
-flagpd1("mcheck-zero-division"),
-flagpd1("mcldemote"),
-flagpd1("mclflushopt"),
-flagpd1("mclwb"),
-flagpd1("mclzero"),
-flagpd1("mcmodel=medany"),
-flagpd1("mcmodel=medlow"),
-flagpd1("mcmpb"),
-flagpd1("mcmse"),
-sepd1("mcode-model"),
-flagpd1("mcode-object-v3"),
-flagpd1("mconstant-cfstrings"),
-flagpd1("mconstructor-aliases"),
-flagpd1("mcpu=?"),
-flagpd1("mcrbits"),
-flagpd1("mcrc"),
-flagpd1("mcumode"),
-flagpd1("mcx16"),
-sepd1("mdebug-pass"),
-flagpd1("mdirect-move"),
-flagpd1("mdisable-tail-calls"),
-flagpd1("mdouble-float"),
-flagpd1("mdsp"),
-flagpd1("mdspr2"),
-sepd1("meabi"),
-flagpd1("membedded-data"),
-flagpd1("menable-no-infs"),
-flagpd1("menable-no-nans"),
-flagpd1("menable-unsafe-fp-math"),
-flagpd1("menqcmd"),
-flagpd1("fmerge-constants"),
-flagpd1("fno-merge-constants"),
-flagpd1("mexception-handling"),
-flagpd1("mexecute-only"),
-flagpd1("mextern-sdata"),
-flagpd1("mf16c"),
-flagpd1("mfancy-math-387"),
-flagpd1("mfentry"),
-flagpd1("mfix-and-continue"),
-flagpd1("mfix-cortex-a53-835769"),
-flagpd1("mfloat128"),
-sepd1("mfloat-abi"),
-flagpd1("mfma"),
-flagpd1("mfma4"),
-flagpd1("mfp32"),
-flagpd1("mfp64"),
-sepd1("mfpmath"),
-flagpd1("mfprnd"),
-flagpd1("mfpxx"),
-flagpd1("mfsgsbase"),
-flagpd1("mfxsr"),
-flagpd1("mgeneral-regs-only"),
-flagpd1("mgfni"),
-flagpd1("mginv"),
-flagpd1("mglibc"),
-flagpd1("mglobal-merge"),
-flagpd1("mgpopt"),
-flagpd1("mhard-float"),
-flagpd1("mhvx"),
-flagpd1("mhtm"),
-flagpd1("miamcu"),
-flagpd1("mieee-fp"),
-flagpd1("mieee-rnd-near"),
-flagpd1("migrate"),
-flagpd1("no-finalize-removal"),
-flagpd1("no-ns-alloc-error"),
-flagpd1("mimplicit-float"),
-flagpd1("mincremental-linker-compatible"),
-flagpd1("minline-all-stringops"),
-flagpd1("minvariant-function-descriptors"),
-flagpd1("minvpcid"),
-flagpd1("mips1"),
-flagpd1("mips16"),
-flagpd1("mips2"),
-flagpd1("mips3"),
-flagpd1("mips32"),
-flagpd1("mips32r2"),
-flagpd1("mips32r3"),
-flagpd1("mips32r5"),
-flagpd1("mips32r6"),
-flagpd1("mips4"),
-flagpd1("mips5"),
-flagpd1("mips64"),
-flagpd1("mips64r2"),
-flagpd1("mips64r3"),
-flagpd1("mips64r5"),
-flagpd1("mips64r6"),
-flagpd1("misel"),
-flagpd1("mkernel"),
-flagpd1("mldc1-sdc1"),
-sepd1("mlimit-float-precision"),
-sepd1("mlink-bitcode-file"),
-sepd1("mlink-builtin-bitcode"),
-sepd1("mlink-cuda-bitcode"),
-flagpd1("mlittle-endian"),
-sepd1("mllvm"),
-flagpd1("mlocal-sdata"),
-flagpd1("mlong-calls"),
-flagpd1("mlong-double-128"),
-flagpd1("mlong-double-64"),
-flagpd1("mlong-double-80"),
-flagpd1("mlongcall"),
-flagpd1("mlwp"),
-flagpd1("mlzcnt"),
-flagpd1("mmadd4"),
-flagpd1("mmemops"),
-flagpd1("mmfcrf"),
-flagpd1("mmfocrf"),
-flagpd1("mmicromips"),
-flagpd1("mmmx"),
-flagpd1("mmovbe"),
-flagpd1("mmovdir64b"),
-flagpd1("mmovdiri"),
-flagpd1("mmpx"),
-flagpd1("mms-bitfields"),
-flagpd1("mmsa"),
-flagpd1("mmt"),
-flagpd1("mmultivalue"),
-flagpd1("mmutable-globals"),
-flagpd1("mmwaitx"),
-flagpd1("mno-3dnow"),
-flagpd1("mno-3dnowa"),
-flagpd1("mno-80387"),
-flagpd1("mno-abicalls"),
-flagpd1("mno-adx"),
-flagpd1("mno-aes"),
-flagpd1("mno-altivec"),
-flagpd1("mno-atomics"),
-flagpd1("mno-avx"),
-flagpd1("mno-avx2"),
-flagpd1("mno-avx512bf16"),
-flagpd1("mno-avx512bitalg"),
-flagpd1("mno-avx512bw"),
-flagpd1("mno-avx512cd"),
-flagpd1("mno-avx512dq"),
-flagpd1("mno-avx512er"),
-flagpd1("mno-avx512f"),
-flagpd1("mno-avx512ifma"),
-flagpd1("mno-avx512pf"),
-flagpd1("mno-avx512vbmi"),
-flagpd1("mno-avx512vbmi2"),
-flagpd1("mno-avx512vl"),
-flagpd1("mno-avx512vnni"),
-flagpd1("mno-avx512vp2intersect"),
-flagpd1("mno-avx512vpopcntdq"),
-flagpd1("mno-backchain"),
-flagpd1("mno-bmi"),
-flagpd1("mno-bmi2"),
-flagpd1("mno-branch-likely"),
-flagpd1("mno-bulk-memory"),
-flagpd1("mno-check-zero-division"),
-flagpd1("mno-cldemote"),
-flagpd1("mno-clflushopt"),
-flagpd1("mno-clwb"),
-flagpd1("mno-clzero"),
-flagpd1("mno-cmpb"),
-flagpd1("mno-code-object-v3"),
-flagpd1("mno-constant-cfstrings"),
-flagpd1("mno-crbits"),
-flagpd1("mno-crc"),
-flagpd1("mno-cumode"),
-flagpd1("mno-cx16"),
-flagpd1("mno-dsp"),
-flagpd1("mno-dspr2"),
-flagpd1("mno-embedded-data"),
-flagpd1("mno-enqcmd"),
-flagpd1("mno-exception-handling"),
-flagpd1("mnoexecstack"),
-flagpd1("mno-execute-only"),
-flagpd1("mno-extern-sdata"),
-flagpd1("mno-f16c"),
-flagpd1("mno-fix-cortex-a53-835769"),
-flagpd1("mno-float128"),
-flagpd1("mno-fma"),
-flagpd1("mno-fma4"),
-flagpd1("mno-fprnd"),
-flagpd1("mno-fsgsbase"),
-flagpd1("mno-fxsr"),
-flagpd1("mno-gfni"),
-flagpd1("mno-ginv"),
-flagpd1("mno-global-merge"),
-flagpd1("mno-gpopt"),
-flagpd1("mno-hvx"),
-flagpd1("mno-htm"),
-flagpd1("mno-iamcu"),
-flagpd1("mno-implicit-float"),
-flagpd1("mno-incremental-linker-compatible"),
-flagpd1("mno-inline-all-stringops"),
-flagpd1("mno-invariant-function-descriptors"),
-flagpd1("mno-invpcid"),
-flagpd1("mno-isel"),
-flagpd1("mno-ldc1-sdc1"),
-flagpd1("mno-local-sdata"),
-flagpd1("mno-long-calls"),
-flagpd1("mno-longcall"),
-flagpd1("mno-lwp"),
-flagpd1("mno-lzcnt"),
-flagpd1("mno-madd4"),
-flagpd1("mno-memops"),
-flagpd1("mno-mfcrf"),
-flagpd1("mno-mfocrf"),
-flagpd1("mno-micromips"),
-flagpd1("mno-mips16"),
-flagpd1("mno-mmx"),
-flagpd1("mno-movbe"),
-flagpd1("mno-movdir64b"),
-flagpd1("mno-movdiri"),
-flagpd1("mno-movt"),
-flagpd1("mno-mpx"),
-flagpd1("mno-ms-bitfields"),
-flagpd1("mno-msa"),
-flagpd1("mno-mt"),
-flagpd1("mno-multivalue"),
-flagpd1("mno-mutable-globals"),
-flagpd1("mno-mwaitx"),
-flagpd1("mno-neg-immediates"),
-flagpd1("mno-nontrapping-fptoint"),
-flagpd1("mno-nvj"),
-flagpd1("mno-nvs"),
-flagpd1("mno-odd-spreg"),
-flagpd1("mno-omit-leaf-frame-pointer"),
-flagpd1("mno-outline"),
-flagpd1("mno-packed-stack"),
-flagpd1("mno-packets"),
-flagpd1("mno-pascal-strings"),
-flagpd1("mno-pclmul"),
-flagpd1("mno-pconfig"),
-flagpd1("mno-pie-copy-relocations"),
-flagpd1("mno-pku"),
-flagpd1("mno-popcnt"),
-flagpd1("mno-popcntd"),
-flagpd1("mno-power8-vector"),
-flagpd1("mno-power9-vector"),
-flagpd1("mno-prefetchwt1"),
-flagpd1("mno-prfchw"),
-flagpd1("mno-ptwrite"),
-flagpd1("mno-pure-code"),
-flagpd1("mno-qpx"),
-flagpd1("mno-rdpid"),
-flagpd1("mno-rdrnd"),
-flagpd1("mno-rdseed"),
-flagpd1("mno-red-zone"),
-flagpd1("mno-reference-types"),
-flagpd1("mno-relax"),
-flagpd1("mno-relax-all"),
-flagpd1("mno-relax-pic-calls"),
-flagpd1("mno-restrict-it"),
-flagpd1("mno-retpoline"),
-flagpd1("mno-retpoline-external-thunk"),
-flagpd1("mno-rtd"),
-flagpd1("mno-rtm"),
-flagpd1("mno-sahf"),
-flagpd1("mno-save-restore"),
-flagpd1("mno-sgx"),
-flagpd1("mno-sha"),
-flagpd1("mno-shstk"),
-flagpd1("mno-sign-ext"),
-flagpd1("mno-simd128"),
-flagpd1("mno-soft-float"),
-flagpd1("mno-spe"),
-flagpd1("mno-speculative-load-hardening"),
-flagpd1("mno-sram-ecc"),
-flagpd1("mno-sse"),
-flagpd1("mno-sse2"),
-flagpd1("mno-sse3"),
-flagpd1("mno-sse4"),
-flagpd1("mno-sse4.1"),
-flagpd1("mno-sse4.2"),
-flagpd1("mno-sse4a"),
-flagpd1("mno-ssse3"),
-flagpd1("mno-stack-arg-probe"),
-flagpd1("mno-stackrealign"),
-flagpd1("mno-tail-call"),
-flagpd1("mno-tbm"),
-flagpd1("mno-thumb"),
-flagpd1("mno-tls-direct-seg-refs"),
-flagpd1("mno-unaligned-access"),
-flagpd1("mno-unimplemented-simd128"),
-flagpd1("mno-vaes"),
-flagpd1("mno-virt"),
-flagpd1("mno-vpclmulqdq"),
-flagpd1("mno-vsx"),
-flagpd1("mno-vx"),
-flagpd1("mno-vzeroupper"),
-flagpd1("mno-waitpkg"),
-flagpd1("mno-warn-nonportable-cfstrings"),
-flagpd1("mno-wavefrontsize64"),
-flagpd1("mno-wbnoinvd"),
-flagpd1("mno-x87"),
-flagpd1("mno-xgot"),
-flagpd1("mno-xnack"),
-flagpd1("mno-xop"),
-flagpd1("mno-xsave"),
-flagpd1("mno-xsavec"),
-flagpd1("mno-xsaveopt"),
-flagpd1("mno-xsaves"),
-flagpd1("mno-zero-initialized-in-bss"),
-flagpd1("mno-zvector"),
-flagpd1("mnocrc"),
-flagpd1("mno-direct-move"),
-flagpd1("mnontrapping-fptoint"),
-flagpd1("mnop-mcount"),
-flagpd1("mno-crypto"),
-flagpd1("mnvj"),
-flagpd1("mnvs"),
-flagpd1("modd-spreg"),
-sepd1("module-dependency-dir"),
-flagpd1("module-file-deps"),
-flagpd1("module-file-info"),
-flagpd1("fmodule-private"),
-flagpd1("fno-module-private"),
-flagpd1("fmodulo-sched-allow-regmoves"),
-flagpd1("fno-modulo-sched-allow-regmoves"),
-flagpd1("fmodulo-sched"),
-flagpd1("fno-modulo-sched"),
-flagpd1("momit-leaf-frame-pointer"),
-flagpd1("moutline"),
-flagpd1("mpacked-stack"),
-flagpd1("mpackets"),
-flagpd1("mpascal-strings"),
-flagpd1("mpclmul"),
-flagpd1("mpconfig"),
-flagpd1("mpie-copy-relocations"),
-flagpd1("mpku"),
-flagpd1("mpopcnt"),
-flagpd1("mpopcntd"),
-flagpd1("mcrypto"),
-flagpd1("mpower8-vector"),
-flagpd1("mpower9-vector"),
-flagpd1("mprefetchwt1"),
-flagpd1("mprfchw"),
-flagpd1("mptwrite"),
-flagpd1("mpure-code"),
-flagpd1("mqdsp6-compat"),
-flagpd1("mqpx"),
-flagpd1("mrdpid"),
-flagpd1("mrdrnd"),
-flagpd1("mrdseed"),
-flagpd1("mreassociate"),
-flagpd1("mrecip"),
-flagpd1("mrecord-mcount"),
-flagpd1("mred-zone"),
-flagpd1("mreference-types"),
-sepd1("mregparm"),
-flagpd1("mrelax"),
-flagpd1("mrelax-all"),
-flagpd1("mrelax-pic-calls"),
-.{
- .name = "mrelax-relocations",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-sepd1("mrelocation-model"),
-flagpd1("mrestrict-it"),
-flagpd1("mretpoline"),
-flagpd1("mretpoline-external-thunk"),
-flagpd1("mrtd"),
-flagpd1("mrtm"),
-flagpd1("msahf"),
-flagpd1("msave-restore"),
-flagpd1("msave-temp-labels"),
-flagpd1("msecure-plt"),
-flagpd1("msgx"),
-flagpd1("msha"),
-flagpd1("mshstk"),
-flagpd1("msign-ext"),
-flagpd1("msimd128"),
-flagpd1("msingle-float"),
-flagpd1("msoft-float"),
-flagpd1("mspe"),
-flagpd1("mspeculative-load-hardening"),
-flagpd1("msram-ecc"),
-flagpd1("msse"),
-flagpd1("msse2"),
-flagpd1("msse3"),
-flagpd1("msse4"),
-flagpd1("msse4.1"),
-flagpd1("msse4.2"),
-flagpd1("msse4a"),
-flagpd1("mssse3"),
-flagpd1("mstack-arg-probe"),
-flagpd1("mstackrealign"),
-flagpd1("mstrict-align"),
-sepd1("mt-migrate-directory"),
-flagpd1("mtail-call"),
-flagpd1("mtbm"),
-sepd1("mthread-model"),
-flagpd1("mthumb"),
-flagpd1("mtls-direct-seg-refs"),
-sepd1("mtp"),
-flagpd1("mtune=?"),
-flagpd1("muclibc"),
-flagpd1("multi_module"),
-sepd1("multiply_defined"),
-sepd1("multiply_defined_unused"),
-flagpd1("munaligned-access"),
-flagpd1("munimplemented-simd128"),
-flagpd1("munwind-tables"),
-flagpd1("mv5"),
-flagpd1("mv55"),
-flagpd1("mv60"),
-flagpd1("mv62"),
-flagpd1("mv65"),
-flagpd1("mv66"),
-flagpd1("mvaes"),
-flagpd1("mvirt"),
-flagpd1("mvpclmulqdq"),
-flagpd1("mvsx"),
-flagpd1("mvx"),
-flagpd1("mvzeroupper"),
-flagpd1("mwaitpkg"),
-flagpd1("mwarn-nonportable-cfstrings"),
-flagpd1("mwavefrontsize64"),
-flagpd1("mwbnoinvd"),
-flagpd1("mx32"),
-flagpd1("mx87"),
-flagpd1("mxgot"),
-flagpd1("mxnack"),
-flagpd1("mxop"),
-flagpd1("mxsave"),
-flagpd1("mxsavec"),
-flagpd1("mxsaveopt"),
-flagpd1("mxsaves"),
-flagpd1("mzvector"),
-flagpd1("n"),
-flagpd1("new-struct-path-tbaa"),
-flagpd1("no_dead_strip_inits_and_terms"),
-flagpd1("no-canonical-prefixes"),
-flagpd1("no-code-completion-globals"),
-flagpd1("no-code-completion-ns-level-decls"),
-flagpd1("no-cpp-precomp"),
-.{
- .name = "no-cuda-noopt-device-debug",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "no-cuda-version-check",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("no-emit-llvm-uselists"),
-flagpd1("no-implicit-float"),
-.{
- .name = "no-integrated-cpp",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "no-pedantic",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("no-pie"),
-flagpd1("no-pthread"),
-flagpd1("no-struct-path-tbaa"),
-flagpd1("nobuiltininc"),
-flagpd1("nocpp"),
-flagpd1("nocudainc"),
-flagpd1("nodefaultlibs"),
-flagpd1("nofixprebinding"),
-flagpd1("nogpulib"),
-.{
- .name = "nolibc",
- .syntax = .flag,
- .zig_equivalent = .nostdlib,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("nomultidefs"),
-flagpd1("fnon-call-exceptions"),
-flagpd1("fno-non-call-exceptions"),
-flagpd1("nopie"),
-flagpd1("noprebind"),
-flagpd1("noprofilelib"),
-flagpd1("noseglinkedit"),
-flagpd1("nostartfiles"),
-.{
- .name = "nostdinc",
- .syntax = .flag,
- .zig_equivalent = .nostdlibinc,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "nostdinc++",
- .syntax = .flag,
- .zig_equivalent = .nostdlib_cpp,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "nostdlib",
- .syntax = .flag,
- .zig_equivalent = .nostdlib,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "nostdlibinc",
- .syntax = .flag,
- .zig_equivalent = .nostdlibinc,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "nostdlib++",
- .syntax = .flag,
- .zig_equivalent = .nostdlib_cpp,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("nostdsysteminc"),
-flagpd1("objcmt-atomic-property"),
-flagpd1("objcmt-migrate-all"),
-flagpd1("objcmt-migrate-annotation"),
-flagpd1("objcmt-migrate-designated-init"),
-flagpd1("objcmt-migrate-instancetype"),
-flagpd1("objcmt-migrate-literals"),
-flagpd1("objcmt-migrate-ns-macros"),
-flagpd1("objcmt-migrate-property"),
-flagpd1("objcmt-migrate-property-dot-syntax"),
-flagpd1("objcmt-migrate-protocol-conformance"),
-flagpd1("objcmt-migrate-readonly-property"),
-flagpd1("objcmt-migrate-readwrite-property"),
-flagpd1("objcmt-migrate-subscripting"),
-flagpd1("objcmt-ns-nonatomic-iosonly"),
-flagpd1("objcmt-returns-innerpointer-property"),
-flagpd1("object"),
-sepd1("opt-record-file"),
-sepd1("opt-record-format"),
-sepd1("opt-record-passes"),
-sepd1("output-asm-variant"),
-flagpd1("p"),
-flagpd1("fpack-derived"),
-flagpd1("fno-pack-derived"),
-.{
- .name = "pass-exit-codes",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("pch-through-hdrstop-create"),
-flagpd1("pch-through-hdrstop-use"),
-.{
- .name = "pedantic",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "pedantic-errors",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("fpeel-loops"),
-flagpd1("fno-peel-loops"),
-flagpd1("fpermissive"),
-flagpd1("fno-permissive"),
-flagpd1("pg"),
-flagpd1("pic-is-pie"),
-sepd1("pic-level"),
-flagpd1("pie"),
-.{
- .name = "pipe",
- .syntax = .flag,
- .zig_equivalent = .ignore,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-sepd1("plugin"),
-flagpd1("prebind"),
-flagpd1("prebind_all_twolevel_modules"),
-flagpd1("fprefetch-loop-arrays"),
-flagpd1("fno-prefetch-loop-arrays"),
-flagpd1("preload"),
-flagpd1("print-dependency-directives-minimized-source"),
-.{
- .name = "print-effective-triple",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("print-ivar-layout"),
-.{
- .name = "print-libgcc-file-name",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "print-multi-directory",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "print-multi-lib",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "print-multi-os-directory",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("print-preamble"),
-.{
- .name = "print-resource-dir",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "print-search-dirs",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("print-stats"),
-.{
- .name = "print-supported-cpus",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "print-target-triple",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("fprintf"),
-flagpd1("fno-printf"),
-flagpd1("private_bundle"),
-flagpd1("fprofile-correction"),
-flagpd1("fno-profile-correction"),
-flagpd1("fprofile"),
-flagpd1("fno-profile"),
-flagpd1("fprofile-generate-sampling"),
-flagpd1("fno-profile-generate-sampling"),
-flagpd1("fprofile-reusedist"),
-flagpd1("fno-profile-reusedist"),
-flagpd1("fprofile-values"),
-flagpd1("fno-profile-values"),
-flagpd1("fprotect-parens"),
-flagpd1("fno-protect-parens"),
-flagpd1("pthread"),
-flagpd1("pthreads"),
-flagpd1("r"),
-flagpd1("frange-check"),
-flagpd1("fno-range-check"),
-.{
- .name = "rdynamic",
- .syntax = .flag,
- .zig_equivalent = .rdynamic,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-sepd1("read_only_relocs"),
-flagpd1("freal-4-real-10"),
-flagpd1("fno-real-4-real-10"),
-flagpd1("freal-4-real-16"),
-flagpd1("fno-real-4-real-16"),
-flagpd1("freal-4-real-8"),
-flagpd1("fno-real-4-real-8"),
-flagpd1("freal-8-real-10"),
-flagpd1("fno-real-8-real-10"),
-flagpd1("freal-8-real-16"),
-flagpd1("fno-real-8-real-16"),
-flagpd1("freal-8-real-4"),
-flagpd1("fno-real-8-real-4"),
-flagpd1("frealloc-lhs"),
-flagpd1("fno-realloc-lhs"),
-sepd1("record-command-line"),
-flagpd1("frecursive"),
-flagpd1("fno-recursive"),
-flagpd1("fregs-graph"),
-flagpd1("fno-regs-graph"),
-flagpd1("relaxed-aliasing"),
-.{
- .name = "relocatable-pch",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("remap"),
-sepd1("remap-file"),
-flagpd1("frename-registers"),
-flagpd1("fno-rename-registers"),
-flagpd1("freorder-blocks"),
-flagpd1("fno-reorder-blocks"),
-flagpd1("frepack-arrays"),
-flagpd1("fno-repack-arrays"),
-sepd1("resource-dir"),
-flagpd1("rewrite-legacy-objc"),
-flagpd1("rewrite-macros"),
-flagpd1("rewrite-objc"),
-flagpd1("rewrite-test"),
-flagpd1("fripa"),
-flagpd1("fno-ripa"),
-sepd1("rpath"),
-flagpd1("s"),
-.{
- .name = "save-stats",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "save-temps",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("fschedule-insns2"),
-flagpd1("fno-schedule-insns2"),
-flagpd1("fschedule-insns"),
-flagpd1("fno-schedule-insns"),
-flagpd1("fsecond-underscore"),
-flagpd1("fno-second-underscore"),
-.{
- .name = "sectalign",
- .syntax = .{.multi_arg=3},
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "sectcreate",
- .syntax = .{.multi_arg=3},
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "sectobjectsymbols",
- .syntax = .{.multi_arg=2},
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "sectorder",
- .syntax = .{.multi_arg=3},
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("fsee"),
-flagpd1("fno-see"),
-sepd1("seg_addr_table"),
-sepd1("seg_addr_table_filename"),
-.{
- .name = "segaddr",
- .syntax = .{.multi_arg=2},
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "segcreate",
- .syntax = .{.multi_arg=3},
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("seglinkedit"),
-.{
- .name = "segprot",
- .syntax = .{.multi_arg=3},
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-sepd1("segs_read_only_addr"),
-sepd1("segs_read_write_addr"),
-flagpd1("setup-static-analyzer"),
-.{
- .name = "shared",
- .syntax = .flag,
- .zig_equivalent = .shared,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("shared-libgcc"),
-flagpd1("shared-libsan"),
-flagpd1("show-encoding"),
-.{
- .name = "show-includes",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("show-inst"),
-flagpd1("fsign-zero"),
-flagpd1("fno-sign-zero"),
-flagpd1("fsignaling-nans"),
-flagpd1("fno-signaling-nans"),
-flagpd1("single_module"),
-flagpd1("fsingle-precision-constant"),
-flagpd1("fno-single-precision-constant"),
-flagpd1("fspec-constr-count"),
-flagpd1("fno-spec-constr-count"),
-.{
- .name = "specs",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-sepd1("split-dwarf-file"),
-sepd1("split-dwarf-output"),
-flagpd1("split-stacks"),
-flagpd1("fstack-arrays"),
-flagpd1("fno-stack-arrays"),
-flagpd1("fstack-check"),
-flagpd1("fno-stack-check"),
-sepd1("stack-protector"),
-sepd1("stack-protector-buffer-size"),
-.{
- .name = "static",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("static-define"),
-flagpd1("static-libgcc"),
-flagpd1("static-libgfortran"),
-flagpd1("static-libsan"),
-flagpd1("static-libstdc++"),
-flagpd1("static-openmp"),
-flagpd1("static-pie"),
-flagpd1("fstrength-reduce"),
-flagpd1("fno-strength-reduce"),
-flagpd1("sys-header-deps"),
-flagpd1("t"),
-sepd1("target-abi"),
-sepd1("target-cpu"),
-sepd1("target-feature"),
-.{
- .name = "target",
- .syntax = .separate,
- .zig_equivalent = .target,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-sepd1("target-linker-version"),
-flagpd1("templight-dump"),
-flagpd1("test-coverage"),
-flagpd1("time"),
-flagpd1("ftls-model"),
-flagpd1("fno-tls-model"),
-flagpd1("ftracer"),
-flagpd1("fno-tracer"),
-.{
- .name = "traditional",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "traditional-cpp",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("ftree-dce"),
-flagpd1("fno-tree-dce"),
-flagpd1("ftree_loop_im"),
-flagpd1("fno-tree_loop_im"),
-flagpd1("ftree_loop_ivcanon"),
-flagpd1("fno-tree_loop_ivcanon"),
-flagpd1("ftree_loop_linear"),
-flagpd1("fno-tree_loop_linear"),
-flagpd1("ftree-salias"),
-flagpd1("fno-tree-salias"),
-flagpd1("ftree-ter"),
-flagpd1("fno-tree-ter"),
-flagpd1("ftree-vectorizer-verbose"),
-flagpd1("fno-tree-vectorizer-verbose"),
-flagpd1("ftree-vrp"),
-flagpd1("fno-tree-vrp"),
-.{
- .name = "trigraphs",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("trim-egraph"),
-sepd1("triple"),
-flagpd1("twolevel_namespace"),
-flagpd1("twolevel_namespace_hints"),
-sepd1("umbrella"),
-flagpd1("undef"),
-flagpd1("funderscoring"),
-flagpd1("fno-underscoring"),
-sepd1("unexported_symbols_list"),
-flagpd1("funroll-all-loops"),
-flagpd1("fno-unroll-all-loops"),
-flagpd1("funsafe-loop-optimizations"),
-flagpd1("fno-unsafe-loop-optimizations"),
-flagpd1("funswitch-loops"),
-flagpd1("fno-unswitch-loops"),
-flagpd1("fuse-linker-plugin"),
-flagpd1("fno-use-linker-plugin"),
-flagpd1("v"),
-flagpd1("fvariable-expansion-in-unroller"),
-flagpd1("fno-variable-expansion-in-unroller"),
-flagpd1("fvect-cost-model"),
-flagpd1("fno-vect-cost-model"),
-flagpd1("vectorize-loops"),
-flagpd1("vectorize-slp"),
-flagpd1("verify"),
-.{
- .name = "verify-debug-info",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("verify-ignore-unexpected"),
-flagpd1("verify-pch"),
-flagpd1("version"),
-.{
- .name = "via-file-asm",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("w"),
-sepd1("weak_framework"),
-sepd1("weak_library"),
-sepd1("weak_reference_mismatches"),
-flagpd1("fweb"),
-flagpd1("fno-web"),
-flagpd1("whatsloaded"),
-flagpd1("fwhole-file"),
-flagpd1("fno-whole-file"),
-flagpd1("fwhole-program"),
-flagpd1("fno-whole-program"),
-flagpd1("whyload"),
-.{
- .name = "z",
- .syntax = .separate,
- .zig_equivalent = .linker_input_z,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("fsanitize-undefined-strip-path-components="),
-joinpd1("fopenmp-cuda-teams-reduction-recs-num="),
-joinpd1("analyzer-config-compatibility-mode="),
-joinpd1("fpatchable-function-entry-offset="),
-joinpd1("analyzer-inline-max-stack-depth="),
-joinpd1("fsanitize-address-field-padding="),
-joinpd1("fdiagnostics-hotness-threshold="),
-joinpd1("fsanitize-memory-track-origins="),
-joinpd1("mwatchos-simulator-version-min="),
-joinpd1("mappletvsimulator-version-min="),
-joinpd1("fobjc-nonfragile-abi-version="),
-joinpd1("fprofile-instrument-use-path="),
-jspd1("fxray-instrumentation-bundle="),
-joinpd1("miphonesimulator-version-min="),
-joinpd1("faddress-space-map-mangling="),
-joinpd1("foptimization-record-passes="),
-joinpd1("ftest-module-file-extension="),
-jspd1("fxray-instruction-threshold="),
-joinpd1("mno-default-build-attributes"),
-joinpd1("mtvos-simulator-version-min="),
-joinpd1("mwatchsimulator-version-min="),
-.{
- .name = "include-with-prefix-before=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("objcmt-white-list-dir-path="),
-joinpd1("error-on-deserialized-decl="),
-joinpd1("fconstexpr-backtrace-limit="),
-joinpd1("fdiagnostics-show-category="),
-joinpd1("fdiagnostics-show-location="),
-joinpd1("fopenmp-cuda-blocks-per-sm="),
-joinpd1("fsanitize-system-blacklist="),
-jspd1("fxray-instruction-threshold"),
-joinpd1("headerpad_max_install_names"),
-joinpd1("mios-simulator-version-min="),
-.{
- .name = "include-with-prefix-after=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("fms-compatibility-version="),
-joinpd1("fopenmp-cuda-number-of-sm="),
-joinpd1("foptimization-record-file="),
-joinpd1("fpatchable-function-entry="),
-joinpd1("fsave-optimization-record="),
-joinpd1("ftemplate-backtrace-limit="),
-.{
- .name = "gpu-max-threads-per-block=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("malign-branch-prefix-size="),
-joinpd1("objcmt-whitelist-dir-path="),
-joinpd1("Wno-nonportable-cfstrings"),
-joinpd1("analyzer-disable-checker="),
-joinpd1("fbuild-session-timestamp="),
-joinpd1("fprofile-instrument-path="),
-joinpd1("mdefault-build-attributes"),
-joinpd1("msign-return-address-key="),
-.{
- .name = "verify-ignore-unexpected=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "include-directory-after=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "compress-debug-sections=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "fcomment-block-commands=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("flax-vector-conversions="),
-joinpd1("fmodules-embed-all-files"),
-joinpd1("fmodules-prune-interval="),
-joinpd1("foverride-record-layout="),
-joinpd1("fprofile-instr-generate="),
-joinpd1("fprofile-remapping-file="),
-joinpd1("fsanitize-coverage-type="),
-joinpd1("fsanitize-hwaddress-abi="),
-joinpd1("ftime-trace-granularity="),
-jspd1("fxray-always-instrument="),
-jspd1("internal-externc-isystem"),
-.{
- .name = "libomptarget-nvptx-path=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "no-system-header-prefix=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "output-class-directory=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("analyzer-inlining-mode="),
-joinpd1("fconstant-string-class="),
-joinpd1("fcrash-diagnostics-dir="),
-joinpd1("fdebug-compilation-dir="),
-joinpd1("fdebug-default-version="),
-joinpd1("ffp-exception-behavior="),
-joinpd1("fmacro-backtrace-limit="),
-joinpd1("fmax-array-constructor="),
-joinpd1("fprofile-exclude-files="),
-joinpd1("ftrivial-auto-var-init="),
-jspd1("fxray-never-instrument="),
-jspd1("interface-stub-version="),
-joinpd1("malign-branch-boundary="),
-joinpd1("mappletvos-version-min="),
-joinpd1("Wnonportable-cfstrings"),
-joinpd1("fdefault-calling-conv="),
-joinpd1("fmax-subrecord-length="),
-joinpd1("fmodules-ignore-macro="),
-.{
- .name = "fno-sanitize-coverage=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("fobjc-dispatch-method="),
-joinpd1("foperator-arrow-depth="),
-joinpd1("fprebuilt-module-path="),
-joinpd1("fprofile-filter-files="),
-joinpd1("fspell-checking-limit="),
-joinpd1("miphoneos-version-min="),
-joinpd1("msmall-data-threshold="),
-joinpd1("Wlarge-by-value-copy="),
-joinpd1("analyzer-constraints="),
-joinpd1("analyzer-dump-egraph="),
-jspd1("compatibility_version"),
-jspd1("dylinker_install_name"),
-joinpd1("fcs-profile-generate="),
-joinpd1("fmodules-prune-after="),
-.{
- .name = "fno-sanitize-recover=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-jspd1("iframeworkwithsysroot"),
-joinpd1("mamdgpu-debugger-abi="),
-joinpd1("mprefer-vector-width="),
-joinpd1("msign-return-address="),
-joinpd1("mwatchos-version-min="),
-.{
- .name = "system-header-prefix=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include-with-prefix=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("coverage-notes-file="),
-joinpd1("fbuild-session-file="),
-joinpd1("fdiagnostics-format="),
-joinpd1("fmax-stack-var-size="),
-joinpd1("fmodules-cache-path="),
-joinpd1("fmodules-embed-file="),
-joinpd1("fprofile-instrument="),
-joinpd1("fprofile-sample-use="),
-joinpd1("fsanitize-blacklist="),
-.{
- .name = "hip-device-lib-path=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("mmacosx-version-min="),
-.{
- .name = "no-cuda-include-ptx=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("Wframe-larger-than="),
-joinpd1("code-completion-at="),
-joinpd1("coverage-data-file="),
-joinpd1("fblas-matmul-limit="),
-joinpd1("fdiagnostics-color="),
-joinpd1("ffixed-line-length-"),
-joinpd1("flimited-precision="),
-joinpd1("fprofile-instr-use="),
-.{
- .name = "fsanitize-coverage=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("fthin-link-bitcode="),
-joinpd1("mbranch-protection="),
-joinpd1("mmacos-version-min="),
-joinpd1("pch-through-header="),
-joinpd1("target-sdk-version="),
-.{
- .name = "execution-charset:",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "include-directory=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "library-directory=",
- .syntax = .joined,
- .zig_equivalent = .lib_dir,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "config-system-dir=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("fclang-abi-compat="),
-joinpd1("fcompile-resource="),
-joinpd1("fdebug-prefix-map="),
-joinpd1("fdenormal-fp-math="),
-joinpd1("fexcess-precision="),
-joinpd1("ffree-line-length-"),
-joinpd1("fmacro-prefix-map="),
-.{
- .name = "fno-sanitize-trap=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("fobjc-abi-version="),
-joinpd1("foutput-class-dir="),
-joinpd1("fprofile-generate="),
-joinpd1("frewrite-map-file="),
-.{
- .name = "fsanitize-recover=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("fsymbol-partition="),
-joinpd1("mcompact-branches="),
-joinpd1("mstack-probe-size="),
-joinpd1("mtvos-version-min="),
-joinpd1("working-directory="),
-joinpd1("analyze-function="),
-joinpd1("analyzer-checker="),
-joinpd1("coverage-version="),
-.{
- .name = "cuda-include-ptx=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("falign-functions="),
-joinpd1("fconstexpr-depth="),
-joinpd1("fconstexpr-steps="),
-joinpd1("ffile-prefix-map="),
-joinpd1("fmodule-map-file="),
-joinpd1("fobjc-arc-cxxlib="),
-jspd1("iwithprefixbefore"),
-joinpd1("malign-functions="),
-joinpd1("mios-version-min="),
-joinpd1("mstack-alignment="),
-.{
- .name = "no-cuda-gpu-arch=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-jspd1("working-directory"),
-joinpd1("analyzer-output="),
-.{
- .name = "config-user-dir=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("debug-info-kind="),
-joinpd1("debugger-tuning="),
-joinpd1("fcf-runtime-abi="),
-joinpd1("finit-character="),
-joinpd1("fmax-type-align="),
-joinpd1("fmessage-length="),
-.{
- .name = "fopenmp-targets=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("fopenmp-version="),
-joinpd1("fshow-overloads="),
-joinpd1("ftemplate-depth-"),
-joinpd1("ftemplate-depth="),
-jspd1("fxray-attr-list="),
-jspd1("internal-isystem"),
-joinpd1("mlinker-version="),
-.{
- .name = "print-file-name=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "print-prog-name=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-jspd1("stdlib++-isystem"),
-joinpd1("Rpass-analysis="),
-.{
- .name = "Xopenmp-target=",
- .syntax = .joined_and_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "source-charset:",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "analyzer-output",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include-prefix=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "undefine-macro=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("analyzer-purge="),
-joinpd1("analyzer-store="),
-jspd1("current_version"),
-joinpd1("fbootclasspath="),
-joinpd1("fbracket-depth="),
-joinpd1("fcf-protection="),
-joinpd1("fdepfile-entry="),
-joinpd1("fembed-bitcode="),
-joinpd1("finput-charset="),
-joinpd1("fmodule-format="),
-joinpd1("fms-memptr-rep="),
-joinpd1("fnew-alignment="),
-joinpd1("frecord-marker="),
-.{
- .name = "fsanitize-trap=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("fthinlto-index="),
-joinpd1("ftrap-function="),
-joinpd1("ftrapv-handler="),
-.{
- .name = "hip-device-lib=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("mdynamic-no-pic"),
-joinpd1("mframe-pointer="),
-joinpd1("mindirect-jump="),
-joinpd1("preamble-bytes="),
-.{
- .name = "bootclasspath=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "cuda-gpu-arch=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "dependent-lib=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("dwarf-version="),
-joinpd1("falign-labels="),
-joinpd1("fauto-profile="),
-joinpd1("fexec-charset="),
-joinpd1("fgnuc-version="),
-joinpd1("finit-integer="),
-joinpd1("finit-logical="),
-joinpd1("finline-limit="),
-joinpd1("fobjc-runtime="),
-.{
- .name = "gcc-toolchain=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "linker-option=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "malign-branch=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-jspd1("objcxx-isystem"),
-joinpd1("vtordisp-mode="),
-joinpd1("Rpass-missed="),
-joinpd1("Wlarger-than-"),
-joinpd1("Wlarger-than="),
-.{
- .name = "define-macro=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("ast-dump-all="),
-.{
- .name = "autocomplete=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("falign-jumps="),
-joinpd1("falign-loops="),
-joinpd1("faligned-new="),
-joinpd1("ferror-limit="),
-joinpd1("ffp-contract="),
-joinpd1("fmodule-file="),
-joinpd1("fmodule-name="),
-joinpd1("fmsc-version="),
-.{
- .name = "fno-sanitize=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("fpack-struct="),
-joinpd1("fpass-plugin="),
-joinpd1("fprofile-dir="),
-joinpd1("fprofile-use="),
-joinpd1("frandom-seed="),
-joinpd1("gsplit-dwarf="),
-jspd1("isystem-after"),
-joinpd1("malign-jumps="),
-joinpd1("malign-loops="),
-joinpd1("mimplicit-it="),
-jspd1("pagezero_size"),
-joinpd1("resource-dir="),
-.{
- .name = "dyld-prefix=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "driver-mode=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("fmax-errors="),
-joinpd1("fno-builtin-"),
-joinpd1("fvisibility="),
-joinpd1("fwchar-type="),
-jspd1("fxray-modes="),
-jspd1("iwithsysroot"),
-joinpd1("mhvx-length="),
-jspd1("objc-isystem"),
-.{
- .name = "rsp-quoting=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("std-default="),
-jspd1("sub_umbrella"),
-.{
- .name = "Qpar-report",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Qvec-report",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "errorReport",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "for-linker=",
- .syntax = .joined,
- .zig_equivalent = .for_linker,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "force-link=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-jspd1("client_name"),
-jspd1("cxx-isystem"),
-joinpd1("fclasspath="),
-joinpd1("finit-real="),
-joinpd1("fforce-addr"),
-joinpd1("ftls-model="),
-jspd1("ivfsoverlay"),
-jspd1("iwithprefix"),
-joinpd1("mfloat-abi="),
-.{
- .name = "plugin-arg-",
- .syntax = .joined_and_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "ptxas-path=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "save-stats=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "save-temps=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("stats-file="),
-jspd1("sub_library"),
-.{
- .name = "CLASSPATH=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "constexpr:",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "classpath=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "cuda-path=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("fencoding="),
-joinpd1("ffp-model="),
-joinpd1("ffpe-trap="),
-joinpd1("flto-jobs="),
-.{
- .name = "fsanitize=",
- .syntax = .comma_joined,
- .zig_equivalent = .sanitize,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-jspd1("iframework"),
-joinpd1("mtls-size="),
-joinpd1("segs_read_"),
-.{
- .name = "unwindlib=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "cgthreads",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "encoding=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "language=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "optimize=",
- .syntax = .joined,
- .zig_equivalent = .optimize,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "resource=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("ast-dump="),
-jspd1("c-isystem"),
-joinpd1("fcoarray="),
-joinpd1("fconvert="),
-joinpd1("fextdirs="),
-joinpd1("ftabstop="),
-jspd1("idirafter"),
-joinpd1("mregparm="),
-jspd1("undefined"),
-.{
- .name = "extdirs=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "imacros=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "sysroot=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("fopenmp="),
-joinpd1("fplugin="),
-joinpd1("fuse-ld="),
-joinpd1("fveclib="),
-jspd1("isysroot"),
-joinpd1("mcmodel="),
-joinpd1("mconsole"),
-joinpd1("mfpmath="),
-joinpd1("mhwmult="),
-joinpd1("mthreads"),
-joinpd1("municode"),
-joinpd1("mwindows"),
-jspd1("seg1addr"),
-.{
- .name = "assert=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "mhwdiv=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "output=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "prefix=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "cl-ext=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("cl-std="),
-joinpd1("fcheck="),
-.{
- .name = "imacros",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-jspd1("iprefix"),
-jspd1("isystem"),
-joinpd1("mhwdiv="),
-joinpd1("moslib="),
-.{
- .name = "mrecip=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "stdlib=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "target=",
- .syntax = .joined,
- .zig_equivalent = .target,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("triple="),
-.{
- .name = "verify=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("Rpass="),
-.{
- .name = "Xarch_",
- .syntax = .joined_and_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "clang:",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "guard:",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "debug=",
- .syntax = .joined,
- .zig_equivalent = .debug,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "param=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "warn-=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("fixit="),
-joinpd1("gstabs"),
-joinpd1("gxcoff"),
-jspd1("iquote"),
-.{
- .name = "march=",
- .syntax = .joined,
- .zig_equivalent = .mcpu,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "mtune=",
- .syntax = .joined,
- .zig_equivalent = .mcpu,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "rtlib=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "specs=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("weak-l"),
-.{
- .name = "Ofast",
- .syntax = .joined,
- .zig_equivalent = .optimize,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-jspd1("Tdata"),
-jspd1("Ttext"),
-.{
- .name = "arch:",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "favor",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "imsvc",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "warn-",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("flto="),
-joinpd1("gcoff"),
-joinpd1("mabi="),
-joinpd1("mabs="),
-joinpd1("masm="),
-.{
- .name = "mcpu=",
- .syntax = .joined,
- .zig_equivalent = .mcpu,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("mfpu="),
-joinpd1("mhvx="),
-joinpd1("mmcu="),
-joinpd1("mnan="),
-jspd1("Tbss"),
-.{
- .name = "link",
- .syntax = .remaining_args_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "std:",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-joinpd1("ccc-"),
-joinpd1("gvms"),
-joinpd1("mdll"),
-joinpd1("mtp="),
-.{
- .name = "std=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "Wa,",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "Wl,",
- .syntax = .comma_joined,
- .zig_equivalent = .wl,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "Wp,",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "RTC",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "clr",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "doc",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-joinpd1("gz="),
-joinpd1("A-"),
-joinpd1("G="),
-.{
- .name = "MF",
- .syntax = .joined_or_separate,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "MJ",
- .syntax = .joined_or_separate,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "MQ",
- .syntax = .joined_or_separate,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "MT",
- .syntax = .joined_or_separate,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "AI",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "EH",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "FA",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "FI",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "FR",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "FU",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Fa",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Fd",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Fe",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Fi",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Fm",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Fo",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Fp",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Fr",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gs",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "MP",
- .syntax = .joined,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Tc",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Tp",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Yc",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Yl",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Yu",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "ZW",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zm",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zp",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "d2",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "vd",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-jspd1("A"),
-jspd1("B"),
-jspd1("D"),
-.{
- .name = "F",
- .syntax = .joined_or_separate,
- .zig_equivalent = .framework_dir,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-jspd1("G"),
-jspd1("I"),
-jspd1("J"),
-.{
- .name = "L",
- .syntax = .joined_or_separate,
- .zig_equivalent = .lib_dir,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "O",
- .syntax = .joined,
- .zig_equivalent = .optimize,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("R"),
-.{
- .name = "T",
- .syntax = .joined_or_separate,
- .zig_equivalent = .linker_script,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-jspd1("U"),
-jspd1("V"),
-joinpd1("W"),
-joinpd1("X"),
-joinpd1("Z"),
-.{
- .name = "D",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "F",
- .syntax = .joined_or_separate,
- .zig_equivalent = .framework_dir,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "I",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "O",
- .syntax = .joined,
- .zig_equivalent = .optimize,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "U",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "o",
- .syntax = .joined_or_separate,
- .zig_equivalent = .o,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "w",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-joinpd1("a"),
-jspd1("b"),
-joinpd1("d"),
-jspd1("e"),
-.{
- .name = "l",
- .syntax = .joined_or_separate,
- .zig_equivalent = .l,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "o",
- .syntax = .joined_or_separate,
- .zig_equivalent = .o,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-jspd1("u"),
-jspd1("x"),
-joinpd1("y"),
-};};
diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig
deleted file mode 100644
index 9405a5f72c1e87169c4e6c2b00afb8a33b1fe40f..0000000000000000000000000000000000000000
--- a/src-self-hosted/codegen.zig
+++ /dev/null
@@ -1,2795 +0,0 @@
-const std = @import("std");
-const mem = std.mem;
-const math = std.math;
-const assert = std.debug.assert;
-const ir = @import("ir.zig");
-const Type = @import("type.zig").Type;
-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 Target = std.Target;
-const Allocator = mem.Allocator;
-const trace = @import("tracy.zig").trace;
-const DW = std.dwarf;
-const leb128 = std.debug.leb;
-const log = std.log.scoped(.codegen);
-
-// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
-// zig fmt: off
-
-/// The codegen-related data that is stored in `ir.Inst.Block` instructions.
-pub const BlockData = struct {
- relocs: std.ArrayListUnmanaged(Reloc) = undefined,
- /// The first break instruction encounters `null` here and chooses a
- /// machine code value for the block result, populating this field.
- /// Following break instructions encounter that value and use it for
- /// the location to store their block results.
- mcv: AnyMCValue = undefined,
-};
-
-/// Architecture-independent MCValue. Here, we have a type that is the same size as
-/// the architecture-specific MCValue. Next to the declaration of MCValue is a
-/// comptime assert that makes sure we guessed correctly about the size. This only
-/// exists so that we can bitcast an arch-independent field to and from the real MCValue.
-pub const AnyMCValue = extern struct {
- a: u64,
- b: u64,
-};
-
-pub const Reloc = union(enum) {
- /// The value is an offset into the `Function` `code` from the beginning.
- /// To perform the reloc, write 32-bit signed little-endian integer
- /// which is a relative jump, based on the address following the reloc.
- rel32: usize,
-};
-
-pub const Result = union(enum) {
- /// The `code` parameter passed to `generateSymbol` has the value appended.
- appended: void,
- /// The value is available externally, `code` is unused.
- externally_managed: []const u8,
- fail: *Module.ErrorMsg,
-};
-
-pub const GenerateSymbolError = error{
- OutOfMemory,
- /// A Decl that this symbol depends on had a semantic analysis failure.
- AnalysisFail,
-};
-
-pub const DebugInfoOutput = union(enum) {
- dwarf: struct {
- dbg_line: *std.ArrayList(u8),
- dbg_info: *std.ArrayList(u8),
- dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable,
- },
- none,
-};
-
-pub fn generateSymbol(
- bin_file: *link.File,
- src: usize,
- typed_value: TypedValue,
- code: *std.ArrayList(u8),
- debug_output: DebugInfoOutput,
-) GenerateSymbolError!Result {
- const tracy = trace(@src());
- defer tracy.end();
-
- switch (typed_value.ty.zigTypeTag()) {
- .Fn => {
- switch (bin_file.options.target.cpu.arch) {
- .wasm32 => unreachable, // has its own code path
- .wasm64 => unreachable, // has its own code path
- .arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, debug_output),
- .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, debug_output),
- .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, debug_output),
- .spu_2 => return Function(.spu_2).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code, debug_output),
- .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code, debug_output),
- else => @panic("Backend architectures that don't have good support yet are commented out, to improve compilation performance. If you are interested in one of these other backends feel free to uncomment them. Eventually these will be completed, but stage1 is slow and a memory hog."),
- }
- },
- .Array => {
- // TODO populate .debug_info for the array
- if (typed_value.val.cast(Value.Payload.Bytes)) |payload| {
- if (typed_value.ty.sentinel()) |sentinel| {
- try code.ensureCapacity(code.items.len + payload.data.len + 1);
- code.appendSliceAssumeCapacity(payload.data);
- const prev_len = code.items.len;
- switch (try generateSymbol(bin_file, src, .{
- .ty = typed_value.ty.elemType(),
- .val = sentinel,
- }, code, debug_output)) {
- .appended => return Result{ .appended = {} },
- .externally_managed => |slice| {
- code.appendSliceAssumeCapacity(slice);
- return Result{ .appended = {} };
- },
- .fail => |em| return Result{ .fail = em },
- }
- } else {
- return Result{ .externally_managed = payload.data };
- }
- }
- return Result{
- .fail = try ErrorMsg.create(
- bin_file.allocator,
- src,
- "TODO implement generateSymbol for more kinds of arrays",
- .{},
- ),
- };
- },
- .Pointer => {
- // TODO populate .debug_info for the pointer
-
- if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
- const decl = payload.decl;
- if (decl.analysis != .complete) return error.AnalysisFail;
- // TODO handle the dependency of this symbol on the decl's vaddr.
- // If the decl changes vaddr, then this symbol needs to get regenerated.
- const vaddr = bin_file.getDeclVAddr(decl);
- const endian = bin_file.options.target.cpu.arch.endian();
- switch (bin_file.options.target.cpu.arch.ptrBitWidth()) {
- 16 => {
- try code.resize(2);
- mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian);
- },
- 32 => {
- try code.resize(4);
- mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian);
- },
- 64 => {
- try code.resize(8);
- mem.writeInt(u64, code.items[0..8], vaddr, endian);
- },
- else => unreachable,
- }
- return Result{ .appended = {} };
- }
- return Result{
- .fail = try ErrorMsg.create(
- bin_file.allocator,
- src,
- "TODO implement generateSymbol for pointer {}",
- .{typed_value.val},
- ),
- };
- },
- .Int => {
- // TODO populate .debug_info for the integer
-
- const info = typed_value.ty.intInfo(bin_file.options.target);
- if (info.bits == 8 and !info.signed) {
- const x = typed_value.val.toUnsignedInt();
- try code.append(@intCast(u8, x));
- return Result{ .appended = {} };
- }
- return Result{
- .fail = try ErrorMsg.create(
- bin_file.allocator,
- src,
- "TODO implement generateSymbol for int type '{}'",
- .{typed_value.ty},
- ),
- };
- },
- else => |t| {
- return Result{
- .fail = try ErrorMsg.create(
- bin_file.allocator,
- src,
- "TODO implement generateSymbol for type '{}'",
- .{@tagName(t)},
- ),
- };
- },
- }
-}
-
-const InnerError = error{
- OutOfMemory,
- CodegenFail,
-};
-
-fn Function(comptime arch: std.Target.Cpu.Arch) type {
- return struct {
- gpa: *Allocator,
- bin_file: *link.File,
- target: *const std.Target,
- mod_fn: *const Module.Fn,
- code: *std.ArrayList(u8),
- debug_output: DebugInfoOutput,
- err_msg: ?*ErrorMsg,
- args: []MCValue,
- ret_mcv: MCValue,
- fn_type: Type,
- arg_index: usize,
- src: usize,
- stack_align: u32,
-
- /// Byte offset within the source file.
- prev_di_src: usize,
- /// Relative to the beginning of `code`.
- prev_di_pc: usize,
- /// Used to find newlines and count line deltas.
- source: []const u8,
- /// Byte offset within the source file of the ending curly.
- rbrace_src: usize,
-
- /// The value is an offset into the `Function` `code` from the beginning.
- /// To perform the reloc, write 32-bit signed little-endian integer
- /// which is a relative jump, based on the address following the reloc.
- exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{},
-
- /// Whenever there is a runtime branch, we push a Branch onto this stack,
- /// and pop it off when the runtime branch joins. This provides an "overlay"
- /// of the table of mappings from instructions to `MCValue` from within the branch.
- /// This way we can modify the `MCValue` for an instruction in different ways
- /// within different branches. Special consideration is needed when a branch
- /// joins with its parent, to make sure all instructions have the same MCValue
- /// across each runtime branch upon joining.
- branch_stack: *std.ArrayList(Branch),
-
- /// The key must be canonical register.
- registers: std.AutoHashMapUnmanaged(Register, *ir.Inst) = .{},
- free_registers: FreeRegInt = math.maxInt(FreeRegInt),
- /// Maps offset to what is stored there.
- stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
-
- /// Offset from the stack base, representing the end of the stack frame.
- max_end_stack: u32 = 0,
- /// Represents the current end stack offset. If there is no existing slot
- /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
- next_stack_offset: u32 = 0,
-
- const MCValue = union(enum) {
- /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
- /// TODO Look into deleting this tag and using `dead` instead, since every use
- /// of MCValue.none should be instead looking at the type and noticing it is 0 bits.
- none,
- /// Control flow will not allow this value to be observed.
- unreach,
- /// No more references to this value remain.
- dead,
- /// The value is undefined.
- undef,
- /// A pointer-sized integer that fits in a register.
- /// If the type is a pointer, this is the pointer address in virtual address space.
- immediate: u64,
- /// The constant was emitted into the code, at this offset.
- /// If the type is a pointer, it means the pointer address is embedded in the code.
- embedded_in_code: usize,
- /// The value is a pointer to a constant which was emitted into the code, at this offset.
- ptr_embedded_in_code: usize,
- /// The value is in a target-specific register.
- register: Register,
- /// The value is in memory at a hard-coded address.
- /// If the type is a pointer, it means the pointer address is at this memory location.
- memory: u64,
- /// The value is one of the stack variables.
- /// If the type is a pointer, it means the pointer address is in the stack at this offset.
- stack_offset: u32,
- /// The value is a pointer to one of the stack variables (payload is stack offset).
- ptr_stack_offset: u32,
- /// The value is in the compare flags assuming an unsigned operation,
- /// with this operator applied on top of it.
- compare_flags_unsigned: math.CompareOperator,
- /// The value is in the compare flags assuming a signed operation,
- /// with this operator applied on top of it.
- compare_flags_signed: math.CompareOperator,
-
- fn isMemory(mcv: MCValue) bool {
- return switch (mcv) {
- .embedded_in_code, .memory, .stack_offset => true,
- else => false,
- };
- }
-
- fn isImmediate(mcv: MCValue) bool {
- return switch (mcv) {
- .immediate => true,
- else => false,
- };
- }
-
- fn isMutable(mcv: MCValue) bool {
- return switch (mcv) {
- .none => unreachable,
- .unreach => unreachable,
- .dead => unreachable,
-
- .immediate,
- .embedded_in_code,
- .memory,
- .compare_flags_unsigned,
- .compare_flags_signed,
- .ptr_stack_offset,
- .ptr_embedded_in_code,
- .undef,
- => false,
-
- .register,
- .stack_offset,
- => true,
- };
- }
- };
-
- const Branch = struct {
- inst_table: std.AutoArrayHashMapUnmanaged(*ir.Inst, MCValue) = .{},
-
- fn deinit(self: *Branch, gpa: *Allocator) void {
- self.inst_table.deinit(gpa);
- self.* = undefined;
- }
- };
-
- fn markRegUsed(self: *Self, reg: Register) void {
- if (FreeRegInt == u0) return;
- const index = reg.allocIndex() orelse return;
- const ShiftInt = math.Log2Int(FreeRegInt);
- const shift = @intCast(ShiftInt, index);
- self.free_registers &= ~(@as(FreeRegInt, 1) << shift);
- }
-
- fn markRegFree(self: *Self, reg: Register) void {
- if (FreeRegInt == u0) return;
- const index = reg.allocIndex() orelse return;
- const ShiftInt = math.Log2Int(FreeRegInt);
- const shift = @intCast(ShiftInt, index);
- self.free_registers |= @as(FreeRegInt, 1) << shift;
- }
-
- /// Before calling, must ensureCapacity + 1 on self.registers.
- /// Returns `null` if all registers are allocated.
- fn allocReg(self: *Self, inst: *ir.Inst) ?Register {
- const free_index = @ctz(FreeRegInt, self.free_registers);
- if (free_index >= callee_preserved_regs.len) {
- return null;
- }
- self.free_registers &= ~(@as(FreeRegInt, 1) << free_index);
- const reg = callee_preserved_regs[free_index];
- self.registers.putAssumeCapacityNoClobber(reg, inst);
- log.debug("alloc {} => {*}", .{reg, inst});
- return reg;
- }
-
- /// Does not track the register.
- fn findUnusedReg(self: *Self) ?Register {
- const free_index = @ctz(FreeRegInt, self.free_registers);
- if (free_index >= callee_preserved_regs.len) {
- return null;
- }
- return callee_preserved_regs[free_index];
- }
-
- const StackAllocation = struct {
- inst: *ir.Inst,
- /// TODO do we need size? should be determined by inst.ty.abiSize()
- size: u32,
- };
-
- const Self = @This();
-
- fn generateSymbol(
- bin_file: *link.File,
- src: usize,
- typed_value: TypedValue,
- code: *std.ArrayList(u8),
- debug_output: DebugInfoOutput,
- ) GenerateSymbolError!Result {
- const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
-
- const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;
-
- var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
- defer {
- assert(branch_stack.items.len == 1);
- branch_stack.items[0].deinit(bin_file.allocator);
- branch_stack.deinit();
- }
- try branch_stack.append(.{});
-
- const src_data: struct {lbrace_src: usize, rbrace_src: usize, source: []const u8} = blk: {
- if (module_fn.owner_decl.scope.cast(Module.Scope.Container)) |container_scope| {
- const tree = container_scope.file_scope.contents.tree;
- const fn_proto = tree.root_node.decls()[module_fn.owner_decl.src_index].castTag(.FnProto).?;
- const block = fn_proto.getBodyNode().?.castTag(.Block).?;
- const lbrace_src = tree.token_locs[block.lbrace].start;
- const rbrace_src = tree.token_locs[block.rbrace].start;
- break :blk .{ .lbrace_src = lbrace_src, .rbrace_src = rbrace_src, .source = tree.source };
- } else if (module_fn.owner_decl.scope.cast(Module.Scope.ZIRModule)) |zir_module| {
- const byte_off = zir_module.contents.module.decls[module_fn.owner_decl.src_index].inst.src;
- break :blk .{ .lbrace_src = byte_off, .rbrace_src = byte_off, .source = zir_module.source.bytes };
- } else {
- unreachable;
- }
- };
-
- var function = Self{
- .gpa = bin_file.allocator,
- .target = &bin_file.options.target,
- .bin_file = bin_file,
- .mod_fn = module_fn,
- .code = code,
- .debug_output = debug_output,
- .err_msg = null,
- .args = undefined, // populated after `resolveCallingConventionValues`
- .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
- .fn_type = fn_type,
- .arg_index = 0,
- .branch_stack = &branch_stack,
- .src = src,
- .stack_align = undefined,
- .prev_di_pc = 0,
- .prev_di_src = src_data.lbrace_src,
- .rbrace_src = src_data.rbrace_src,
- .source = src_data.source,
- };
- defer function.registers.deinit(bin_file.allocator);
- defer function.stack.deinit(bin_file.allocator);
- defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
-
- var call_info = function.resolveCallingConventionValues(src, fn_type) catch |err| switch (err) {
- error.CodegenFail => return Result{ .fail = function.err_msg.? },
- else => |e| return e,
- };
- defer call_info.deinit(&function);
-
- function.args = call_info.args;
- function.ret_mcv = call_info.return_value;
- function.stack_align = call_info.stack_align;
- function.max_end_stack = call_info.stack_byte_count;
-
- function.gen() catch |err| switch (err) {
- error.CodegenFail => return Result{ .fail = function.err_msg.? },
- else => |e| return e,
- };
-
- if (function.err_msg) |em| {
- return Result{ .fail = em };
- } else {
- return Result{ .appended = {} };
- }
- }
-
- fn gen(self: *Self) !void {
- switch (arch) {
- .x86_64 => {
- try self.code.ensureCapacity(self.code.items.len + 11);
-
- const cc = self.fn_type.fnCallingConvention();
- if (cc != .Naked) {
- // We want to subtract the aligned stack frame size from rsp here, but we don't
- // yet know how big it will be, so we leave room for a 4-byte stack size.
- // TODO During semantic analysis, check if there are no function calls. If there
- // are none, here we can omit the part where we subtract and then add rsp.
- self.code.appendSliceAssumeCapacity(&[_]u8{
- 0x55, // push rbp
- 0x48, 0x89, 0xe5, // mov rbp, rsp
- 0x48, 0x81, 0xec, // sub rsp, imm32 (with reloc)
- });
- const reloc_index = self.code.items.len;
- self.code.items.len += 4;
-
- try self.dbgSetPrologueEnd();
- try self.genBody(self.mod_fn.analysis.success);
-
- const stack_end = self.max_end_stack;
- if (stack_end > math.maxInt(i32))
- return self.fail(self.src, "too much stack used in call parameters", .{});
- const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);
- mem.writeIntLittle(u32, self.code.items[reloc_index..][0..4], @intCast(u32, aligned_stack_end));
-
- if (self.code.items.len >= math.maxInt(i32)) {
- return self.fail(self.src, "unable to perform relocation: jump too far", .{});
- }
- for (self.exitlude_jump_relocs.items) |jmp_reloc| {
- const amt = self.code.items.len - (jmp_reloc + 4);
- // If it wouldn't jump at all, elide it.
- if (amt == 0) {
- self.code.items.len -= 5;
- continue;
- }
- const s32_amt = @intCast(i32, amt);
- mem.writeIntLittle(i32, self.code.items[jmp_reloc..][0..4], s32_amt);
- }
-
- // Important to be after the possible self.code.items.len -= 5 above.
- try self.dbgSetEpilogueBegin();
-
- try self.code.ensureCapacity(self.code.items.len + 9);
- // add rsp, x
- if (aligned_stack_end > math.maxInt(i8)) {
- // example: 48 81 c4 ff ff ff 7f add rsp,0x7fffffff
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x81, 0xc4 });
- const x = @intCast(u32, aligned_stack_end);
- mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x);
- } else if (aligned_stack_end != 0) {
- // example: 48 83 c4 7f add rsp,0x7f
- const x = @intCast(u8, aligned_stack_end);
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x83, 0xc4, x });
- }
-
- self.code.appendSliceAssumeCapacity(&[_]u8{
- 0x5d, // pop rbp
- 0xc3, // ret
- });
- } else {
- try self.dbgSetPrologueEnd();
- try self.genBody(self.mod_fn.analysis.success);
- try self.dbgSetEpilogueBegin();
- }
- },
- else => {
- try self.dbgSetPrologueEnd();
- try self.genBody(self.mod_fn.analysis.success);
- try self.dbgSetEpilogueBegin();
- },
- }
- // Drop them off at the rbrace.
- try self.dbgAdvancePCAndLine(self.rbrace_src);
- }
-
- fn genBody(self: *Self, body: ir.Body) InnerError!void {
- for (body.instructions) |inst| {
- try self.ensureProcessDeathCapacity(@popCount(@TypeOf(inst.deaths), inst.deaths));
-
- const mcv = try self.genFuncInst(inst);
- if (!inst.isUnused()) {
- log.debug("{*} => {}", .{inst, mcv});
- const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
- try branch.inst_table.putNoClobber(self.gpa, inst, mcv);
- }
-
- var i: ir.Inst.DeathsBitIndex = 0;
- while (inst.getOperand(i)) |operand| : (i += 1) {
- if (inst.operandDies(i))
- self.processDeath(operand);
- }
- }
- }
-
- fn dbgSetPrologueEnd(self: *Self) InnerError!void {
- switch (self.debug_output) {
- .dwarf => |dbg_out| {
- try dbg_out.dbg_line.append(DW.LNS_set_prologue_end);
- try self.dbgAdvancePCAndLine(self.prev_di_src);
- },
- .none => {},
- }
- }
-
- fn dbgSetEpilogueBegin(self: *Self) InnerError!void {
- switch (self.debug_output) {
- .dwarf => |dbg_out| {
- try dbg_out.dbg_line.append(DW.LNS_set_epilogue_begin);
- try self.dbgAdvancePCAndLine(self.prev_di_src);
- },
- .none => {},
- }
- }
-
- fn dbgAdvancePCAndLine(self: *Self, src: usize) InnerError!void {
- self.prev_di_src = src;
- self.prev_di_pc = self.code.items.len;
- switch (self.debug_output) {
- .dwarf => |dbg_out| {
- // TODO Look into improving the performance here by adding a token-index-to-line
- // lookup table, and changing ir.Inst from storing byte offset to token. Currently
- // this involves scanning over the source code for newlines
- // (but only from the previous byte offset to the new one).
- const delta_line = std.zig.lineDelta(self.source, self.prev_di_src, src);
- const delta_pc = self.code.items.len - self.prev_di_pc;
- // TODO Look into using the DWARF special opcodes to compress this data. It lets you emit
- // single-byte opcodes that add different numbers to both the PC and the line number
- // at the same time.
- try dbg_out.dbg_line.ensureCapacity(dbg_out.dbg_line.items.len + 11);
- dbg_out.dbg_line.appendAssumeCapacity(DW.LNS_advance_pc);
- leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable;
- if (delta_line != 0) {
- dbg_out.dbg_line.appendAssumeCapacity(DW.LNS_advance_line);
- leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
- }
- dbg_out.dbg_line.appendAssumeCapacity(DW.LNS_copy);
- },
- .none => {},
- }
- }
-
- /// Asserts there is already capacity to insert into top branch inst_table.
- fn processDeath(self: *Self, inst: *ir.Inst) void {
- if (inst.tag == .constant) return; // Constants are immortal.
- // When editing this function, note that the logic must synchronize with `reuseOperand`.
- const prev_value = self.getResolvedInstValue(inst);
- const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
- branch.inst_table.putAssumeCapacity(inst, .dead);
- switch (prev_value) {
- .register => |reg| {
- const canon_reg = toCanonicalReg(reg);
- _ = self.registers.remove(canon_reg);
- self.markRegFree(canon_reg);
- },
- else => {}, // TODO process stack allocation death
- }
- }
-
- fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
- const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
- try table.ensureCapacity(self.gpa, table.items().len + additional_count);
- }
-
- /// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
- /// after codegen for this symbol is done.
- fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
- switch (self.debug_output) {
- .dwarf => |dbg_out| {
- assert(ty.hasCodeGenBits());
- const index = dbg_out.dbg_info.items.len;
- try dbg_out.dbg_info.resize(index + 4); // DW.AT_type, DW.FORM_ref4
-
- const gop = try dbg_out.dbg_info_type_relocs.getOrPut(self.gpa, ty);
- if (!gop.found_existing) {
- gop.entry.value = .{
- .off = undefined,
- .relocs = .{},
- };
- }
- try gop.entry.value.relocs.append(self.gpa, @intCast(u32, index));
- },
- .none => {},
- }
- }
-
- fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {
- switch (inst.tag) {
- .add => return self.genAdd(inst.castTag(.add).?),
- .alloc => return self.genAlloc(inst.castTag(.alloc).?),
- .arg => return self.genArg(inst.castTag(.arg).?),
- .assembly => return self.genAsm(inst.castTag(.assembly).?),
- .bitcast => return self.genBitCast(inst.castTag(.bitcast).?),
- .block => return self.genBlock(inst.castTag(.block).?),
- .br => return self.genBr(inst.castTag(.br).?),
- .breakpoint => return self.genBreakpoint(inst.src),
- .brvoid => return self.genBrVoid(inst.castTag(.brvoid).?),
- .call => return self.genCall(inst.castTag(.call).?),
- .cmp_lt => return self.genCmp(inst.castTag(.cmp_lt).?, .lt),
- .cmp_lte => return self.genCmp(inst.castTag(.cmp_lte).?, .lte),
- .cmp_eq => return self.genCmp(inst.castTag(.cmp_eq).?, .eq),
- .cmp_gte => return self.genCmp(inst.castTag(.cmp_gte).?, .gte),
- .cmp_gt => return self.genCmp(inst.castTag(.cmp_gt).?, .gt),
- .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq),
- .condbr => return self.genCondBr(inst.castTag(.condbr).?),
- .constant => unreachable, // excluded from function bodies
- .dbg_stmt => return self.genDbgStmt(inst.castTag(.dbg_stmt).?),
- .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?),
- .intcast => return self.genIntCast(inst.castTag(.intcast).?),
- .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?),
- .isnull => return self.genIsNull(inst.castTag(.isnull).?),
- .iserr => return self.genIsErr(inst.castTag(.iserr).?),
- .load => return self.genLoad(inst.castTag(.load).?),
- .loop => return self.genLoop(inst.castTag(.loop).?),
- .not => return self.genNot(inst.castTag(.not).?),
- .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?),
- .ref => return self.genRef(inst.castTag(.ref).?),
- .ret => return self.genRet(inst.castTag(.ret).?),
- .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?),
- .store => return self.genStore(inst.castTag(.store).?),
- .sub => return self.genSub(inst.castTag(.sub).?),
- .unreach => return MCValue{ .unreach = {} },
- .unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?),
- .wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?),
- .varptr => return self.genVarPtr(inst.castTag(.varptr).?),
- }
- }
-
- fn allocMem(self: *Self, inst: *ir.Inst, abi_size: u32, abi_align: u32) !u32 {
- if (abi_align > self.stack_align)
- self.stack_align = abi_align;
- // TODO find a free slot instead of always appending
- const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align);
- self.next_stack_offset = offset + abi_size;
- if (self.next_stack_offset > self.max_end_stack)
- self.max_end_stack = self.next_stack_offset;
- try self.stack.putNoClobber(self.gpa, offset, .{
- .inst = inst,
- .size = abi_size,
- });
- return offset;
- }
-
- /// Use a pointer instruction as the basis for allocating stack memory.
- fn allocMemPtr(self: *Self, inst: *ir.Inst) !u32 {
- const elem_ty = inst.ty.elemType();
- const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
- return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty});
- };
- // TODO swap this for inst.ty.ptrAlign
- const abi_align = elem_ty.abiAlignment(self.target.*);
- return self.allocMem(inst, abi_size, abi_align);
- }
-
- fn allocRegOrMem(self: *Self, inst: *ir.Inst, reg_ok: bool) !MCValue {
- const elem_ty = inst.ty;
- const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
- return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty});
- };
- const abi_align = elem_ty.abiAlignment(self.target.*);
- if (abi_align > self.stack_align)
- self.stack_align = abi_align;
-
- if (reg_ok) {
- // Make sure the type can fit in a register before we try to allocate one.
- const ptr_bits = arch.ptrBitWidth();
- const ptr_bytes: u64 = @divExact(ptr_bits, 8);
- if (abi_size <= ptr_bytes) {
- try self.registers.ensureCapacity(self.gpa, self.registers.count() + 1);
- if (self.allocReg(inst)) |reg| {
- return MCValue{ .register = registerAlias(reg, abi_size) };
- }
- }
- }
- const stack_offset = try self.allocMem(inst, abi_size, abi_align);
- return MCValue{ .stack_offset = stack_offset };
- }
-
- /// Copies a value to a register without tracking the register. The register is not considered
- /// allocated. A second call to `copyToTmpRegister` may return the same register.
- /// This can have a side effect of spilling instructions to the stack to free up a register.
- fn copyToTmpRegister(self: *Self, src: usize, mcv: MCValue) !Register {
- const reg = self.findUnusedReg() orelse b: {
- // We'll take over the first register. Move the instruction that was previously
- // there to a stack allocation.
- const reg = callee_preserved_regs[0];
- const regs_entry = self.registers.remove(reg).?;
- const spilled_inst = regs_entry.value;
-
- const stack_mcv = try self.allocRegOrMem(spilled_inst, false);
- const reg_mcv = self.getResolvedInstValue(spilled_inst);
- assert(reg == toCanonicalReg(reg_mcv.register));
- const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
- try branch.inst_table.put(self.gpa, spilled_inst, stack_mcv);
- try self.genSetStack(src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv);
-
- break :b reg;
- };
- try self.genSetReg(src, reg, mcv);
- return reg;
- }
-
- /// Allocates a new register and copies `mcv` into it.
- /// `reg_owner` is the instruction that gets associated with the register in the register table.
- /// This can have a side effect of spilling instructions to the stack to free up a register.
- fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue {
- try self.registers.ensureCapacity(self.gpa, @intCast(u32, self.registers.count() + 1));
-
- const reg = self.allocReg(reg_owner) orelse b: {
- // We'll take over the first register. Move the instruction that was previously
- // there to a stack allocation.
- const reg = callee_preserved_regs[0];
- const regs_entry = self.registers.getEntry(reg).?;
- const spilled_inst = regs_entry.value;
- regs_entry.value = reg_owner;
-
- const stack_mcv = try self.allocRegOrMem(spilled_inst, false);
- const reg_mcv = self.getResolvedInstValue(spilled_inst);
- assert(reg == toCanonicalReg(reg_mcv.register));
- const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
- try branch.inst_table.put(self.gpa, spilled_inst, stack_mcv);
- try self.genSetStack(reg_owner.src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv);
-
- break :b reg;
- };
- try self.genSetReg(reg_owner.src, reg, mcv);
- return MCValue{ .register = reg };
- }
-
- fn genAlloc(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
- const stack_offset = try self.allocMemPtr(&inst.base);
- return MCValue{ .ptr_stack_offset = stack_offset };
- }
-
- fn genFloatCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- // No side effects, so if it's unreferenced, do nothing.
- if (inst.base.isUnused())
- return MCValue.dead;
- switch (arch) {
- else => return self.fail(inst.base.src, "TODO implement floatCast for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genIntCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- // No side effects, so if it's unreferenced, do nothing.
- if (inst.base.isUnused())
- return MCValue.dead;
-
- const operand = try self.resolveInst(inst.operand);
- const info_a = inst.operand.ty.intInfo(self.target.*);
- const info_b = inst.base.ty.intInfo(self.target.*);
- if (info_a.signed != info_b.signed)
- return self.fail(inst.base.src, "TODO gen intcast sign safety in semantic analysis", .{});
-
- if (info_a.bits == info_b.bits)
- return operand;
-
- switch (arch) {
- else => return self.fail(inst.base.src, "TODO implement intCast for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genNot(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- // No side effects, so if it's unreferenced, do nothing.
- if (inst.base.isUnused())
- return MCValue.dead;
- const operand = try self.resolveInst(inst.operand);
- switch (operand) {
- .dead => unreachable,
- .unreach => unreachable,
- .compare_flags_unsigned => |op| return MCValue{
- .compare_flags_unsigned = switch (op) {
- .gte => .lt,
- .gt => .lte,
- .neq => .eq,
- .lt => .gte,
- .lte => .gt,
- .eq => .neq,
- },
- },
- .compare_flags_signed => |op| return MCValue{
- .compare_flags_signed = switch (op) {
- .gte => .lt,
- .gt => .lte,
- .neq => .eq,
- .lt => .gte,
- .lte => .gt,
- .eq => .neq,
- },
- },
- else => {},
- }
-
- switch (arch) {
- .x86_64 => {
- var imm = ir.Inst.Constant{
- .base = .{
- .tag = .constant,
- .deaths = 0,
- .ty = inst.operand.ty,
- .src = inst.operand.src,
- },
- .val = Value.initTag(.bool_true),
- };
- return try self.genX8664BinMath(&inst.base, inst.operand, &imm.base, 6, 0x30);
- },
- else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genAdd(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
- // No side effects, so if it's unreferenced, do nothing.
- if (inst.base.isUnused())
- return MCValue.dead;
- switch (arch) {
- .x86_64 => {
- return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 0, 0x00);
- },
- else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genUnwrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- // No side effects, so if it's unreferenced, do nothing.
- if (inst.base.isUnused())
- return MCValue.dead;
- switch (arch) {
- else => return self.fail(inst.base.src, "TODO implement unwrap optional for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genWrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- const optional_ty = inst.base.ty;
-
- // No side effects, so if it's unreferenced, do nothing.
- if (inst.base.isUnused())
- return MCValue.dead;
-
- // Optional type is just a boolean true
- if (optional_ty.abiSize(self.target.*) == 1)
- return MCValue{ .immediate = 1 };
-
- switch (arch) {
- else => return self.fail(inst.base.src, "TODO implement wrap optional for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genVarPtr(self: *Self, inst: *ir.Inst.VarPtr) !MCValue {
- // No side effects, so if it's unreferenced, do nothing.
- if (inst.base.isUnused())
- return MCValue.dead;
-
- switch (arch) {
- else => return self.fail(inst.base.src, "TODO implement varptr for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn reuseOperand(self: *Self, inst: *ir.Inst, op_index: ir.Inst.DeathsBitIndex, mcv: MCValue) bool {
- if (!inst.operandDies(op_index))
- return false;
-
- switch (mcv) {
- .register => |reg| {
- // If it's in the registers table, need to associate the register with the
- // new instruction.
- if (self.registers.getEntry(toCanonicalReg(reg))) |entry| {
- entry.value = inst;
- }
- log.debug("reusing {} => {*}", .{reg, inst});
- },
- .stack_offset => |off| {
- log.debug("reusing stack offset {} => {*}", .{off, inst});
- return true;
- },
- else => return false,
- }
-
- // Prevent the operand deaths processing code from deallocating it.
- inst.clearOperandDeath(op_index);
-
- // That makes us responsible for doing the rest of the stuff that processDeath would have done.
- const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
- branch.inst_table.putAssumeCapacity(inst.getOperand(op_index).?, .dead);
-
- return true;
- }
-
- fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- const elem_ty = inst.base.ty;
- if (!elem_ty.hasCodeGenBits())
- return MCValue.none;
- const ptr = try self.resolveInst(inst.operand);
- const is_volatile = inst.operand.ty.isVolatilePtr();
- if (inst.base.isUnused() and !is_volatile)
- return MCValue.dead;
- const dst_mcv: MCValue = blk: {
- if (self.reuseOperand(&inst.base, 0, ptr)) {
- // The MCValue that holds the pointer can be re-used as the value.
- break :blk ptr;
- } else {
- break :blk try self.allocRegOrMem(&inst.base, true);
- }
- };
- switch (ptr) {
- .none => unreachable,
- .undef => unreachable,
- .unreach => unreachable,
- .dead => unreachable,
- .compare_flags_unsigned => unreachable,
- .compare_flags_signed => unreachable,
- .immediate => |imm| try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .memory = imm }),
- .ptr_stack_offset => |off| try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .stack_offset = off }),
- .ptr_embedded_in_code => |off| {
- try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .embedded_in_code = off });
- },
- .embedded_in_code => {
- return self.fail(inst.base.src, "TODO implement loading from MCValue.embedded_in_code", .{});
- },
- .register => {
- return self.fail(inst.base.src, "TODO implement loading from MCValue.register", .{});
- },
- .memory => {
- return self.fail(inst.base.src, "TODO implement loading from MCValue.memory", .{});
- },
- .stack_offset => {
- return self.fail(inst.base.src, "TODO implement loading from MCValue.stack_offset", .{});
- },
- }
- return dst_mcv;
- }
-
- fn genStore(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
- const ptr = try self.resolveInst(inst.lhs);
- const value = try self.resolveInst(inst.rhs);
- const elem_ty = inst.rhs.ty;
- switch (ptr) {
- .none => unreachable,
- .undef => unreachable,
- .unreach => unreachable,
- .dead => unreachable,
- .compare_flags_unsigned => unreachable,
- .compare_flags_signed => unreachable,
- .immediate => |imm| {
- try self.setRegOrMem(inst.base.src, elem_ty, .{ .memory = imm }, value);
- },
- .ptr_stack_offset => |off| {
- try self.genSetStack(inst.base.src, elem_ty, off, value);
- },
- .ptr_embedded_in_code => |off| {
- try self.setRegOrMem(inst.base.src, elem_ty, .{ .embedded_in_code = off }, value);
- },
- .embedded_in_code => {
- return self.fail(inst.base.src, "TODO implement storing to MCValue.embedded_in_code", .{});
- },
- .register => {
- return self.fail(inst.base.src, "TODO implement storing to MCValue.register", .{});
- },
- .memory => {
- return self.fail(inst.base.src, "TODO implement storing to MCValue.memory", .{});
- },
- .stack_offset => {
- return self.fail(inst.base.src, "TODO implement storing to MCValue.stack_offset", .{});
- },
- }
- return .none;
- }
-
- fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
- // No side effects, so if it's unreferenced, do nothing.
- if (inst.base.isUnused())
- return MCValue.dead;
- switch (arch) {
- .x86_64 => {
- return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 5, 0x28);
- },
- else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}),
- }
- }
-
- /// ADD, SUB, XOR, OR, AND
- fn genX8664BinMath(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue {
- try self.code.ensureCapacity(self.code.items.len + 8);
-
- const lhs = try self.resolveInst(op_lhs);
- const rhs = try self.resolveInst(op_rhs);
-
- // There are 2 operands, destination and source.
- // Either one, but not both, can be a memory operand.
- // Source operand can be an immediate, 8 bits or 32 bits.
- // So, if either one of the operands dies with this instruction, we can use it
- // as the result MCValue.
- var dst_mcv: MCValue = undefined;
- var src_mcv: MCValue = undefined;
- var src_inst: *ir.Inst = undefined;
- if (self.reuseOperand(inst, 0, lhs)) {
- // LHS dies; use it as the destination.
- // Both operands cannot be memory.
- src_inst = op_rhs;
- if (lhs.isMemory() and rhs.isMemory()) {
- dst_mcv = try self.copyToNewRegister(inst, lhs);
- src_mcv = rhs;
- } else {
- dst_mcv = lhs;
- src_mcv = rhs;
- }
- } else if (self.reuseOperand(inst, 1, rhs)) {
- // RHS dies; use it as the destination.
- // Both operands cannot be memory.
- src_inst = op_lhs;
- if (lhs.isMemory() and rhs.isMemory()) {
- dst_mcv = try self.copyToNewRegister(inst, rhs);
- src_mcv = lhs;
- } else {
- dst_mcv = rhs;
- src_mcv = lhs;
- }
- } else {
- if (lhs.isMemory()) {
- dst_mcv = try self.copyToNewRegister(inst, lhs);
- src_mcv = rhs;
- src_inst = op_rhs;
- } else {
- dst_mcv = try self.copyToNewRegister(inst, rhs);
- src_mcv = lhs;
- src_inst = op_lhs;
- }
- }
- // This instruction supports only signed 32-bit immediates at most. If the immediate
- // value is larger than this, we put it in a register.
- // A potential opportunity for future optimization here would be keeping track
- // of the fact that the instruction is available both as an immediate
- // and as a register.
- switch (src_mcv) {
- .immediate => |imm| {
- if (imm > math.maxInt(u31)) {
- src_mcv = MCValue{ .register = try self.copyToTmpRegister(src_inst.src, src_mcv) };
- }
- },
- else => {},
- }
-
- try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, opx, mr);
-
- return dst_mcv;
- }
-
- fn genX8664BinMathCode(
- self: *Self,
- src: usize,
- dst_ty: Type,
- dst_mcv: MCValue,
- src_mcv: MCValue,
- opx: u8,
- mr: u8,
- ) !void {
- switch (dst_mcv) {
- .none => unreachable,
- .undef => unreachable,
- .dead, .unreach, .immediate => unreachable,
- .compare_flags_unsigned => unreachable,
- .compare_flags_signed => unreachable,
- .ptr_stack_offset => unreachable,
- .ptr_embedded_in_code => unreachable,
- .register => |dst_reg| {
- switch (src_mcv) {
- .none => unreachable,
- .undef => try self.genSetReg(src, dst_reg, .undef),
- .dead, .unreach => unreachable,
- .ptr_stack_offset => unreachable,
- .ptr_embedded_in_code => unreachable,
- .register => |src_reg| {
- self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });
- self.code.appendSliceAssumeCapacity(&[_]u8{ mr + 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) });
- },
- .immediate => |imm| {
- const imm32 = @intCast(u31, imm); // This case must be handled before calling genX8664BinMathCode.
- // 81 /opx id
- if (imm32 <= math.maxInt(u7)) {
- self.rex(.{ .b = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
- self.code.appendSliceAssumeCapacity(&[_]u8{
- 0x83,
- 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
- @intCast(u8, imm32),
- });
- } else {
- self.rex(.{ .r = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
- self.code.appendSliceAssumeCapacity(&[_]u8{
- 0x81,
- 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
- });
- std.mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), imm32);
- }
- },
- .embedded_in_code, .memory, .stack_offset => {
- return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{});
- },
- .compare_flags_unsigned => {
- return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
- },
- .compare_flags_signed => {
- return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});
- },
- }
- },
- .stack_offset => |off| {
- switch (src_mcv) {
- .none => unreachable,
- .undef => return self.genSetStack(src, dst_ty, off, .undef),
- .dead, .unreach => unreachable,
- .ptr_stack_offset => unreachable,
- .ptr_embedded_in_code => unreachable,
- .register => |src_reg| {
- try self.genX8664ModRMRegToStack(src, dst_ty, off, src_reg, mr + 0x1);
- },
- .immediate => |imm| {
- return self.fail(src, "TODO implement x86 ADD/SUB/CMP source immediate", .{});
- },
- .embedded_in_code, .memory, .stack_offset => {
- return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{});
- },
- .compare_flags_unsigned => {
- return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
- },
- .compare_flags_signed => {
- return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});
- },
- }
- },
- .embedded_in_code, .memory => {
- return self.fail(src, "TODO implement x86 ADD/SUB/CMP destination memory", .{});
- },
- }
- }
-
- fn genX8664ModRMRegToStack(self: *Self, src: usize, ty: Type, off: u32, reg: Register, opcode: u8) !void {
- const abi_size = ty.abiSize(self.target.*);
- const adj_off = off + abi_size;
- try self.code.ensureCapacity(self.code.items.len + 7);
- self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended() });
- const reg_id: u8 = @truncate(u3, reg.id());
- if (adj_off <= 128) {
- // example: 48 89 55 7f mov QWORD PTR [rbp+0x7f],rdx
- const RM = @as(u8, 0b01_000_101) | (reg_id << 3);
- const negative_offset = @intCast(i8, -@intCast(i32, adj_off));
- const twos_comp = @bitCast(u8, negative_offset);
- self.code.appendSliceAssumeCapacity(&[_]u8{ opcode, RM, twos_comp });
- } else if (adj_off <= 2147483648) {
- // example: 48 89 95 80 00 00 00 mov QWORD PTR [rbp+0x80],rdx
- const RM = @as(u8, 0b10_000_101) | (reg_id << 3);
- const negative_offset = @intCast(i32, -@intCast(i33, adj_off));
- const twos_comp = @bitCast(u32, negative_offset);
- self.code.appendSliceAssumeCapacity(&[_]u8{ opcode, RM });
- mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), twos_comp);
- } else {
- return self.fail(src, "stack offset too large", .{});
- }
- }
-
- fn genArg(self: *Self, inst: *ir.Inst.Arg) !MCValue {
- if (FreeRegInt == u0) {
- return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});
- }
- if (inst.base.isUnused())
- return MCValue.dead;
-
- try self.registers.ensureCapacity(self.gpa, self.registers.count() + 1);
-
- const result = self.args[self.arg_index];
- self.arg_index += 1;
-
- const name_with_null = inst.name[0..mem.lenZ(inst.name) + 1];
- switch (result) {
- .register => |reg| {
- self.registers.putAssumeCapacityNoClobber(toCanonicalReg(reg), &inst.base);
- self.markRegUsed(reg);
-
- switch (self.debug_output) {
- .dwarf => |dbg_out| {
- try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 8 + name_with_null.len);
- dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);
- dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT_location, DW.FORM_exprloc
- 1, // ULEB128 dwarf expression length
- reg.dwarfLocOp(),
- });
- try self.addDbgInfoTypeReloc(inst.base.ty); // DW.AT_type, DW.FORM_ref4
- dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string
- },
- .none => {},
- }
- },
- else => {},
- }
- return result;
- }
-
- fn genBreakpoint(self: *Self, src: usize) !MCValue {
- switch (arch) {
- .i386, .x86_64 => {
- try self.code.append(0xcc); // int3
- },
- .riscv64 => {
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ebreak.toU32());
- },
- .spu_2 => {
- try self.code.resize(self.code.items.len + 2);
- var instr = Instruction{ .condition = .always, .input0 = .zero, .input1 = .zero, .modify_flags = false, .output = .discard, .command = .undefined1 };
- mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], @bitCast(u16, instr));
- },
- .arm => {
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bkpt(0).toU32());
- },
- .armeb => {
- mem.writeIntBig(u32, try self.code.addManyAsArray(4), Instruction.bkpt(0).toU32());
- },
- else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
- }
- return .none;
- }
-
- fn genCall(self: *Self, inst: *ir.Inst.Call) !MCValue {
- var info = try self.resolveCallingConventionValues(inst.base.src, inst.func.ty);
- defer info.deinit(self);
-
- // Due to incremental compilation, how function calls are generated depends
- // on linking.
- if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {
- switch (arch) {
- .x86_64 => {
- for (info.args) |mc_arg, arg_i| {
- const arg = inst.args[arg_i];
- const arg_mcv = try self.resolveInst(inst.args[arg_i]);
- // Here we do not use setRegOrMem even though the logic is similar, because
- // the function call will move the stack pointer, so the offsets are different.
- switch (mc_arg) {
- .none => continue,
- .register => |reg| {
- try self.genSetReg(arg.src, reg, arg_mcv);
- // TODO interact with the register allocator to mark the instruction as moved.
- },
- .stack_offset => {
- // Here we need to emit instructions like this:
- // mov qword ptr [rsp + stack_offset], x
- return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
- },
- .ptr_stack_offset => {
- return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
- },
- .ptr_embedded_in_code => {
- return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
- },
- .undef => unreachable,
- .immediate => unreachable,
- .unreach => unreachable,
- .dead => unreachable,
- .embedded_in_code => unreachable,
- .memory => unreachable,
- .compare_flags_signed => unreachable,
- .compare_flags_unsigned => unreachable,
- }
- }
-
- if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
- if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
- const func = func_val.func;
-
- const ptr_bits = self.target.cpu.arch.ptrBitWidth();
- const ptr_bytes: u64 = @divExact(ptr_bits, 8);
- const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
- const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
- break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
- } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
- @intCast(u32, coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes)
- else
- unreachable;
-
- // ff 14 25 xx xx xx xx call [addr]
- try self.code.ensureCapacity(self.code.items.len + 7);
- self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
- mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
- } else {
- return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
- }
- } else {
- return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
- }
- },
- .riscv64 => {
- if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch});
-
- if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
- if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
- const func = func_val.func;
-
- const ptr_bits = self.target.cpu.arch.ptrBitWidth();
- const ptr_bytes: u64 = @divExact(ptr_bits, 8);
- const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
- const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
- break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
- } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
- coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes
- else
- unreachable;
-
- try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr });
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());
- } else {
- return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
- }
- } else {
- return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
- }
- },
- .spu_2 => {
- if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
- if (info.args.len != 0) {
- return self.fail(inst.base.src, "TODO implement call with more than 0 parameters", .{});
- }
- if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
- const func = func_val.func;
- const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
- const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
- break :blk @intCast(u16, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * 2);
- } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
- @intCast(u16, coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * 2)
- else
- unreachable;
-
- const return_type = func.owner_decl.typed_value.most_recent.typed_value.ty.fnReturnType();
- // First, push the return address, then jump; if noreturn, don't bother with the first step
- // TODO: implement packed struct -> u16 at comptime and move the bitcast here
- var instr = Instruction{ .condition = .always, .input0 = .immediate, .input1 = .zero, .modify_flags = false, .output = .jump, .command = .load16 };
- if (return_type.zigTypeTag() == .NoReturn) {
- try self.code.resize(self.code.items.len + 4);
- mem.writeIntLittle(u16, self.code.items[self.code.items.len - 4 ..][0..2], @bitCast(u16, instr));
- mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], got_addr);
- return MCValue.unreach;
- } else {
- try self.code.resize(self.code.items.len + 8);
- var push = Instruction{ .condition = .always, .input0 = .immediate, .input1 = .zero, .modify_flags = false, .output = .push, .command = .ipget };
- mem.writeIntLittle(u16, self.code.items[self.code.items.len - 8 ..][0..2], @bitCast(u16, push));
- mem.writeIntLittle(u16, self.code.items[self.code.items.len - 6 ..][0..2], @as(u16, 4));
- mem.writeIntLittle(u16, self.code.items[self.code.items.len - 4 ..][0..2], @bitCast(u16, instr));
- mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], got_addr);
- switch (return_type.zigTypeTag()) {
- .Void => return MCValue{ .none = {} },
- .NoReturn => unreachable,
- else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}),
- }
- }
- } else {
- return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
- }
- } else {
- return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
- }
- },
- .arm => {
- if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch});
-
- if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
- if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
- const func = func_val.func;
- const ptr_bits = self.target.cpu.arch.ptrBitWidth();
- const ptr_bytes: u64 = @divExact(ptr_bits, 8);
- const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
- const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
- break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
- } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
- coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes
- else
- unreachable;
-
- // TODO only works with leaf functions
- // at the moment, which works fine for
- // Hello World, but not for real code
- // of course. Add pushing lr to stack
- // and popping after call
- try self.genSetReg(inst.base.src, .lr, .{ .memory = got_addr });
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blx(.al, .lr).toU32());
- } else {
- return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
- }
- } else {
- return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
- }
- },
- else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
- }
- } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
- switch (arch) {
- .x86_64 => {
- for (info.args) |mc_arg, arg_i| {
- const arg = inst.args[arg_i];
- const arg_mcv = try self.resolveInst(inst.args[arg_i]);
- // Here we do not use setRegOrMem even though the logic is similar, because
- // the function call will move the stack pointer, so the offsets are different.
- switch (mc_arg) {
- .none => continue,
- .register => |reg| {
- try self.genSetReg(arg.src, reg, arg_mcv);
- // TODO interact with the register allocator to mark the instruction as moved.
- },
- .stack_offset => {
- // Here we need to emit instructions like this:
- // mov qword ptr [rsp + stack_offset], x
- return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
- },
- .ptr_stack_offset => {
- return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
- },
- .ptr_embedded_in_code => {
- return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
- },
- .undef => unreachable,
- .immediate => unreachable,
- .unreach => unreachable,
- .dead => unreachable,
- .embedded_in_code => unreachable,
- .memory => unreachable,
- .compare_flags_signed => unreachable,
- .compare_flags_unsigned => unreachable,
- }
- }
-
- if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
- if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
- const func = func_val.func;
- const got = &macho_file.sections.items[macho_file.got_section_index.?];
- const ptr_bytes = 8;
- const got_addr = @intCast(u32, got.addr + func.owner_decl.link.macho.offset_table_index.? * ptr_bytes);
- // ff 14 25 xx xx xx xx call [addr]
- try self.code.ensureCapacity(self.code.items.len + 7);
- self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
- mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
- } else {
- return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
- }
- } else {
- return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
- }
- },
- .aarch64 => return self.fail(inst.base.src, "TODO implement codegen for call when linking with MachO for aarch64 arch", .{}),
- else => unreachable,
- }
- } else {
- unreachable;
- }
-
- return info.return_value;
- }
-
- fn genRef(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- const operand = try self.resolveInst(inst.operand);
- switch (operand) {
- .unreach => unreachable,
- .dead => unreachable,
- .none => return .none,
-
- .immediate,
- .register,
- .ptr_stack_offset,
- .ptr_embedded_in_code,
- .compare_flags_unsigned,
- .compare_flags_signed,
- => {
- const stack_offset = try self.allocMemPtr(&inst.base);
- try self.genSetStack(inst.base.src, inst.operand.ty, stack_offset, operand);
- return MCValue{ .ptr_stack_offset = stack_offset };
- },
-
- .stack_offset => |offset| return MCValue{ .ptr_stack_offset = offset },
- .embedded_in_code => |offset| return MCValue{ .ptr_embedded_in_code = offset },
- .memory => |vaddr| return MCValue{ .immediate = vaddr },
-
- .undef => return self.fail(inst.base.src, "TODO implement ref on an undefined value", .{}),
- }
- }
-
- fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue {
- const ret_ty = self.fn_type.fnReturnType();
- try self.setRegOrMem(src, ret_ty, self.ret_mcv, mcv);
- switch (arch) {
- .i386 => {
- try self.code.append(0xc3); // ret
- },
- .x86_64 => {
- // TODO when implementing defer, this will need to jump to the appropriate defer expression.
- // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
- // which is available if the jump is 127 bytes or less forward.
- try self.code.resize(self.code.items.len + 5);
- self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32
- try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
- },
- .riscv64 => {
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());
- },
- .arm => {
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32());
- },
- else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
- }
- return .unreach;
- }
-
- fn genRet(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- const operand = try self.resolveInst(inst.operand);
- return self.ret(inst.base.src, operand);
- }
-
- fn genRetVoid(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
- return self.ret(inst.base.src, .none);
- }
-
- fn genCmp(self: *Self, inst: *ir.Inst.BinOp, op: math.CompareOperator) !MCValue {
- // No side effects, so if it's unreferenced, do nothing.
- if (inst.base.isUnused())
- return MCValue.dead;
- switch (arch) {
- .x86_64 => {
- try self.code.ensureCapacity(self.code.items.len + 8);
-
- const lhs = try self.resolveInst(inst.lhs);
- const rhs = try self.resolveInst(inst.rhs);
-
- // There are 2 operands, destination and source.
- // Either one, but not both, can be a memory operand.
- // Source operand can be an immediate, 8 bits or 32 bits.
- const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))
- try self.copyToNewRegister(&inst.base, lhs)
- else
- lhs;
- // This instruction supports only signed 32-bit immediates at most.
- const src_mcv = try self.limitImmediateType(inst.rhs, i32);
-
- try self.genX8664BinMathCode(inst.base.src, inst.base.ty, dst_mcv, src_mcv, 7, 0x38);
- const info = inst.lhs.ty.intInfo(self.target.*);
- if (info.signed) {
- return MCValue{ .compare_flags_signed = op };
- } else {
- return MCValue{ .compare_flags_unsigned = op };
- }
- },
- else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genDbgStmt(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
- try self.dbgAdvancePCAndLine(inst.base.src);
- assert(inst.base.isUnused());
- return MCValue.dead;
- }
-
- fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {
- const cond = try self.resolveInst(inst.condition);
-
- const reloc: Reloc = switch (arch) {
- .i386, .x86_64 => reloc: {
- try self.code.ensureCapacity(self.code.items.len + 6);
-
- const opcode: u8 = switch (cond) {
- .compare_flags_signed => |cmp_op| blk: {
- // Here we map to the opposite opcode because the jump is to the false branch.
- const opcode: u8 = switch (cmp_op) {
- .gte => 0x8c,
- .gt => 0x8e,
- .neq => 0x84,
- .lt => 0x8d,
- .lte => 0x8f,
- .eq => 0x85,
- };
- break :blk opcode;
- },
- .compare_flags_unsigned => |cmp_op| blk: {
- // Here we map to the opposite opcode because the jump is to the false branch.
- const opcode: u8 = switch (cmp_op) {
- .gte => 0x82,
- .gt => 0x86,
- .neq => 0x84,
- .lt => 0x83,
- .lte => 0x87,
- .eq => 0x85,
- };
- break :blk opcode;
- },
- .register => |reg| blk: {
- // test reg, 1
- // TODO detect al, ax, eax
- try self.code.ensureCapacity(self.code.items.len + 4);
- // TODO audit this codegen: we force w = true here to make
- // the value affect the big register
- self.rex(.{ .b = reg.isExtended(), .w = true });
- self.code.appendSliceAssumeCapacity(&[_]u8{
- 0xf6,
- @as(u8, 0xC0) | (0 << 3) | @truncate(u3, reg.id()),
- 0x01,
- });
- break :blk 0x84;
- },
- else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {}", .{ self.target.cpu.arch, @tagName(cond) }),
- };
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });
- const reloc = Reloc{ .rel32 = self.code.items.len };
- self.code.items.len += 4;
- break :reloc reloc;
- },
- else => return self.fail(inst.base.src, "TODO implement condbr {}", .{ self.target.cpu.arch }),
- };
-
- // Capture the state of register and stack allocation state so that we can revert to it.
- const parent_next_stack_offset = self.next_stack_offset;
- const parent_free_registers = self.free_registers;
- var parent_stack = try self.stack.clone(self.gpa);
- defer parent_stack.deinit(self.gpa);
- var parent_registers = try self.registers.clone(self.gpa);
- defer parent_registers.deinit(self.gpa);
-
- try self.branch_stack.append(.{});
-
- const then_deaths = inst.thenDeaths();
- try self.ensureProcessDeathCapacity(then_deaths.len);
- for (then_deaths) |operand| {
- self.processDeath(operand);
- }
- try self.genBody(inst.then_body);
-
- // Revert to the previous register and stack allocation state.
-
- var saved_then_branch = self.branch_stack.pop();
- defer saved_then_branch.deinit(self.gpa);
-
- self.registers.deinit(self.gpa);
- self.registers = parent_registers;
- parent_registers = .{};
-
- self.stack.deinit(self.gpa);
- self.stack = parent_stack;
- parent_stack = .{};
-
- self.next_stack_offset = parent_next_stack_offset;
- self.free_registers = parent_free_registers;
-
- try self.performReloc(inst.base.src, reloc);
- const else_branch = self.branch_stack.addOneAssumeCapacity();
- else_branch.* = .{};
-
- const else_deaths = inst.elseDeaths();
- try self.ensureProcessDeathCapacity(else_deaths.len);
- for (else_deaths) |operand| {
- self.processDeath(operand);
- }
- try self.genBody(inst.else_body);
-
- // At this point, each branch will possibly have conflicting values for where
- // each instruction is stored. They agree, however, on which instructions are alive/dead.
- // We use the first ("then") branch as canonical, and here emit
- // instructions into the second ("else") branch to make it conform.
- // We continue respect the data structure semantic guarantees of the else_branch so
- // that we can use all the code emitting abstractions. This is why at the bottom we
- // assert that parent_branch.free_registers equals the saved_then_branch.free_registers
- // rather than assigning it.
- const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2];
- try parent_branch.inst_table.ensureCapacity(self.gpa, parent_branch.inst_table.items().len +
- else_branch.inst_table.items().len);
- for (else_branch.inst_table.items()) |else_entry| {
- const canon_mcv = if (saved_then_branch.inst_table.remove(else_entry.key)) |then_entry| blk: {
- // The instruction's MCValue is overridden in both branches.
- parent_branch.inst_table.putAssumeCapacity(else_entry.key, then_entry.value);
- if (else_entry.value == .dead) {
- assert(then_entry.value == .dead);
- continue;
- }
- break :blk then_entry.value;
- } else blk: {
- if (else_entry.value == .dead)
- continue;
- // The instruction is only overridden in the else branch.
- var i: usize = self.branch_stack.items.len - 2;
- while (true) {
- i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead?
- if (self.branch_stack.items[i].inst_table.get(else_entry.key)) |mcv| {
- assert(mcv != .dead);
- break :blk mcv;
- }
- }
- };
- log.debug("consolidating else_entry {*} {}=>{}", .{else_entry.key, else_entry.value, canon_mcv});
- // TODO make sure the destination stack offset / register does not already have something
- // going on there.
- try self.setRegOrMem(inst.base.src, else_entry.key.ty, canon_mcv, else_entry.value);
- // TODO track the new register / stack allocation
- }
- try parent_branch.inst_table.ensureCapacity(self.gpa, parent_branch.inst_table.items().len +
- saved_then_branch.inst_table.items().len);
- for (saved_then_branch.inst_table.items()) |then_entry| {
- // We already deleted the items from this table that matched the else_branch.
- // So these are all instructions that are only overridden in the then branch.
- parent_branch.inst_table.putAssumeCapacity(then_entry.key, then_entry.value);
- if (then_entry.value == .dead)
- continue;
- const parent_mcv = blk: {
- var i: usize = self.branch_stack.items.len - 2;
- while (true) {
- i -= 1;
- if (self.branch_stack.items[i].inst_table.get(then_entry.key)) |mcv| {
- assert(mcv != .dead);
- break :blk mcv;
- }
- }
- };
- log.debug("consolidating then_entry {*} {}=>{}", .{then_entry.key, parent_mcv, then_entry.value});
- // TODO make sure the destination stack offset / register does not already have something
- // going on there.
- try self.setRegOrMem(inst.base.src, then_entry.key.ty, parent_mcv, then_entry.value);
- // TODO track the new register / stack allocation
- }
-
- self.branch_stack.pop().deinit(self.gpa);
-
- return MCValue.unreach;
- }
-
- fn genIsNull(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- switch (arch) {
- else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genIsNonNull(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- // Here you can specialize this instruction if it makes sense to, otherwise the default
- // will call genIsNull and invert the result.
- switch (arch) {
- else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}),
- }
- }
-
- fn genIsErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- switch (arch) {
- else => return self.fail(inst.base.src, "TODO implement iserr for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue {
- // A loop is a setup to be able to jump back to the beginning.
- const start_index = self.code.items.len;
- try self.genBody(inst.body);
- try self.jump(inst.base.src, start_index);
- return MCValue.unreach;
- }
-
- /// Send control flow to the `index` of `self.code`.
- fn jump(self: *Self, src: usize, index: usize) !void {
- switch (arch) {
- .i386, .x86_64 => {
- try self.code.ensureCapacity(self.code.items.len + 5);
- if (math.cast(i8, @intCast(i32, index) - (@intCast(i32, self.code.items.len + 2)))) |delta| {
- self.code.appendAssumeCapacity(0xeb); // jmp rel8
- self.code.appendAssumeCapacity(@bitCast(u8, delta));
- } else |_| {
- const delta = @intCast(i32, index) - (@intCast(i32, self.code.items.len + 5));
- self.code.appendAssumeCapacity(0xe9); // jmp rel32
- mem.writeIntLittle(i32, self.code.addManyAsArrayAssumeCapacity(4), delta);
- }
- },
- else => return self.fail(src, "TODO implement jump for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genBlock(self: *Self, inst: *ir.Inst.Block) !MCValue {
- inst.codegen = .{
- // A block is a setup to be able to jump to the end.
- .relocs = .{},
- // It also acts as a receptical for break operands.
- // Here we use `MCValue.none` to represent a null value so that the first
- // break instruction will choose a MCValue for the block result and overwrite
- // this field. Following break instructions will use that MCValue to put their
- // block results.
- .mcv = @bitCast(AnyMCValue, MCValue { .none = {} }),
- };
- defer inst.codegen.relocs.deinit(self.gpa);
-
- try self.genBody(inst.body);
-
- for (inst.codegen.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc);
-
- return @bitCast(MCValue, inst.codegen.mcv);
- }
-
- fn performReloc(self: *Self, src: usize, reloc: Reloc) !void {
- switch (reloc) {
- .rel32 => |pos| {
- const amt = self.code.items.len - (pos + 4);
- // Here it would be tempting to implement testing for amt == 0 and then elide the
- // jump. However, that will cause a problem because other jumps may assume that they
- // can jump to this code. Or maybe I didn't understand something when I was debugging.
- // It could be worth another look. Anyway, that's why that isn't done here. Probably the
- // best place to elide jumps will be in semantic analysis, by inlining blocks that only
- // only have 1 break instruction.
- const s32_amt = math.cast(i32, amt) catch
- return self.fail(src, "unable to perform relocation: jump too far", .{});
- mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
- },
- }
- }
-
- fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue {
- if (inst.operand.ty.hasCodeGenBits()) {
- const operand = try self.resolveInst(inst.operand);
- const block_mcv = @bitCast(MCValue, inst.block.codegen.mcv);
- if (block_mcv == .none) {
- inst.block.codegen.mcv = @bitCast(AnyMCValue, operand);
- } else {
- try self.setRegOrMem(inst.base.src, inst.block.base.ty, block_mcv, operand);
- }
- }
- return self.brVoid(inst.base.src, inst.block);
- }
-
- fn genBrVoid(self: *Self, inst: *ir.Inst.BrVoid) !MCValue {
- return self.brVoid(inst.base.src, inst.block);
- }
-
- fn brVoid(self: *Self, src: usize, block: *ir.Inst.Block) !MCValue {
- // Emit a jump with a relocation. It will be patched up after the block ends.
- try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1);
-
- switch (arch) {
- .i386, .x86_64 => {
- // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
- // which is available if the jump is 127 bytes or less forward.
- try self.code.resize(self.code.items.len + 5);
- self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32
- // Leave the jump offset undefined
- block.codegen.relocs.appendAssumeCapacity(.{ .rel32 = self.code.items.len - 4 });
- },
- else => return self.fail(src, "TODO implement brvoid for {}", .{self.target.cpu.arch}),
- }
- return .none;
- }
-
- fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue {
- if (!inst.is_volatile and inst.base.isUnused())
- return MCValue.dead;
- switch (arch) {
- .spu_2 => {
- if (inst.inputs.len > 0 or inst.output != null) {
- return self.fail(inst.base.src, "TODO implement inline asm inputs / outputs for SPU Mark II", .{});
- }
- if (mem.eql(u8, inst.asm_source, "undefined0")) {
- try self.code.resize(self.code.items.len + 2);
- var instr = Instruction{ .condition = .always, .input0 = .zero, .input1 = .zero, .modify_flags = false, .output = .discard, .command = .undefined0 };
- mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], @bitCast(u16, instr));
- return MCValue.none;
- } else {
- return self.fail(inst.base.src, "TODO implement support for more SPU II assembly instructions", .{});
- }
- },
- .arm => {
- for (inst.inputs) |input, i| {
- if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
- return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
- }
- const reg_name = input[1 .. input.len - 1];
- const reg = parseRegName(reg_name) orelse
- return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
- const arg = try self.resolveInst(inst.args[i]);
- try self.genSetReg(inst.base.src, reg, arg);
- }
-
- if (mem.eql(u8, inst.asm_source, "svc #0")) {
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(.al, 0).toU32());
- } else {
- return self.fail(inst.base.src, "TODO implement support for more arm assembly instructions", .{});
- }
-
- if (inst.output) |output| {
- if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
- return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
- }
- const reg_name = output[2 .. output.len - 1];
- const reg = parseRegName(reg_name) orelse
- return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
- return MCValue{ .register = reg };
- } else {
- return MCValue.none;
- }
- },
- .riscv64 => {
- for (inst.inputs) |input, i| {
- if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
- return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
- }
- const reg_name = input[1 .. input.len - 1];
- const reg = parseRegName(reg_name) orelse
- return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
- const arg = try self.resolveInst(inst.args[i]);
- try self.genSetReg(inst.base.src, reg, arg);
- }
-
- if (mem.eql(u8, inst.asm_source, "ecall")) {
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ecall.toU32());
- } else {
- return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{});
- }
-
- if (inst.output) |output| {
- if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
- return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
- }
- const reg_name = output[2 .. output.len - 1];
- const reg = parseRegName(reg_name) orelse
- return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
- return MCValue{ .register = reg };
- } else {
- return MCValue.none;
- }
- },
- .x86_64, .i386 => {
- for (inst.inputs) |input, i| {
- if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
- return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
- }
- const reg_name = input[1 .. input.len - 1];
- const reg = parseRegName(reg_name) orelse
- return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
- const arg = try self.resolveInst(inst.args[i]);
- try self.genSetReg(inst.base.src, reg, arg);
- }
-
- if (mem.eql(u8, inst.asm_source, "syscall")) {
- try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
- } else if (inst.asm_source.len != 0) {
- return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
- }
-
- if (inst.output) |output| {
- if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
- return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
- }
- const reg_name = output[2 .. output.len - 1];
- const reg = parseRegName(reg_name) orelse
- return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
- return MCValue{ .register = reg };
- } else {
- return MCValue.none;
- }
- },
- else => return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{}),
- }
- }
-
- /// Encodes a REX prefix as specified, and appends it to the instruction
- /// stream. This only modifies the instruction stream if at least one bit
- /// is set true, which has a few implications:
- ///
- /// * The length of the instruction buffer will be modified *if* the
- /// resulting REX is meaningful, but will remain the same if it is not.
- /// * Deliberately inserting a "meaningless REX" requires explicit usage of
- /// 0x40, and cannot be done via this function.
- /// W => 64 bit mode
- /// R => extension to the MODRM.reg field
- /// X => extension to the SIB.index field
- /// B => extension to the MODRM.rm field or the SIB.base field
- fn rex(self: *Self, arg: struct { b: bool = false, w: bool = false, x: bool = false, r: bool = false }) void {
- comptime assert(arch == .x86_64);
- // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB.
- var value: u8 = 0x40;
- if (arg.b) {
- value |= 0x1;
- }
- if (arg.x) {
- value |= 0x2;
- }
- if (arg.r) {
- value |= 0x4;
- }
- if (arg.w) {
- value |= 0x8;
- }
- if (value != 0x40) {
- self.code.appendAssumeCapacity(value);
- }
- }
-
- /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
- fn setRegOrMem(self: *Self, src: usize, ty: Type, loc: MCValue, val: MCValue) !void {
- switch (loc) {
- .none => return,
- .register => |reg| return self.genSetReg(src, reg, val),
- .stack_offset => |off| return self.genSetStack(src, ty, off, val),
- .memory => {
- return self.fail(src, "TODO implement setRegOrMem for memory", .{});
- },
- else => unreachable,
- }
- }
-
- fn genSetStack(self: *Self, src: usize, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
- switch (arch) {
- .x86_64 => switch (mcv) {
- .dead => unreachable,
- .ptr_stack_offset => unreachable,
- .ptr_embedded_in_code => unreachable,
- .unreach, .none => return, // Nothing to do.
- .undef => {
- if (!self.wantSafety())
- return; // The already existing value will do just fine.
- // TODO Upgrade this to a memset call when we have that available.
- switch (ty.abiSize(self.target.*)) {
- 1 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaa }),
- 2 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaa }),
- 4 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
- 8 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
- else => return self.fail(src, "TODO implement memset", .{}),
- }
- },
- .compare_flags_unsigned => |op| {
- return self.fail(src, "TODO implement set stack variable with compare flags value (unsigned)", .{});
- },
- .compare_flags_signed => |op| {
- return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{});
- },
- .immediate => |x_big| {
- const abi_size = ty.abiSize(self.target.*);
- const adj_off = stack_offset + abi_size;
- if (adj_off > 128) {
- return self.fail(src, "TODO implement set stack variable with large stack offset", .{});
- }
- try self.code.ensureCapacity(self.code.items.len + 8);
- switch (abi_size) {
- 1 => {
- return self.fail(src, "TODO implement set abi_size=1 stack variable with immediate", .{});
- },
- 2 => {
- return self.fail(src, "TODO implement set abi_size=2 stack variable with immediate", .{});
- },
- 4 => {
- const x = @intCast(u32, x_big);
- // We have a positive stack offset value but we want a twos complement negative
- // offset from rbp, which is at the top of the stack frame.
- const negative_offset = @intCast(i8, -@intCast(i32, adj_off));
- const twos_comp = @bitCast(u8, negative_offset);
- // mov DWORD PTR [rbp+offset], immediate
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0xc7, 0x45, twos_comp });
- mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x);
- },
- 8 => {
- // We have a positive stack offset value but we want a twos complement negative
- // offset from rbp, which is at the top of the stack frame.
- const negative_offset = @intCast(i8, -@intCast(i32, adj_off));
- const twos_comp = @bitCast(u8, negative_offset);
-
- // 64 bit write to memory would take two mov's anyways so we
- // insted just use two 32 bit writes to avoid register allocation
- try self.code.ensureCapacity(self.code.items.len + 14);
- var buf: [8]u8 = undefined;
- mem.writeIntLittle(u64, &buf, x_big);
-
- // mov DWORD PTR [rbp+offset+4], immediate
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0xc7, 0x45, twos_comp + 4});
- self.code.appendSliceAssumeCapacity(buf[4..8]);
-
- // mov DWORD PTR [rbp+offset], immediate
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0xc7, 0x45, twos_comp });
- self.code.appendSliceAssumeCapacity(buf[0..4]);
- },
- else => {
- return self.fail(src, "TODO implement set abi_size=large stack variable with immediate", .{});
- },
- }
- },
- .embedded_in_code => |code_offset| {
- return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{});
- },
- .register => |reg| {
- try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89);
- },
- .memory => |vaddr| {
- return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});
- },
- .stack_offset => |off| {
- if (stack_offset == off)
- return; // Copy stack variable to itself; nothing to do.
-
- const reg = try self.copyToTmpRegister(src, mcv);
- return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
- },
- },
- else => return self.fail(src, "TODO implement getSetStack for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void {
- switch (arch) {
- .arm => switch (mcv) {
- .dead => unreachable,
- .ptr_stack_offset => unreachable,
- .ptr_embedded_in_code => unreachable,
- .unreach, .none => return, // Nothing to do.
- .undef => {
- if (!self.wantSafety())
- return; // The already existing value will do just fine.
- // Write the debug undefined value.
- return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa });
- },
- .immediate => |x| {
- // TODO better analysis of x to determine the
- // least amount of necessary instructions (use
- // more intelligent rotating)
- if (x <= math.maxInt(u8)) {
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
- return;
- } else if (x <= math.maxInt(u16)) {
- // TODO Use movw Note: Not supported on
- // all ARM targets!
-
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
- } else if (x <= math.maxInt(u32)) {
- // TODO Use movw and movt Note: Not
- // supported on all ARM targets! Also TODO
- // write constant to code and load
- // relative to pc
-
- // immediate: 0xaabbccdd
- // mov reg, #0xaa
- // orr reg, reg, #0xbb, 24
- // orr reg, reg, #0xcc, 16
- // orr reg, reg, #0xdd, 8
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32());
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32());
- return;
- } else {
- return self.fail(src, "ARM registers are 32-bit wide", .{});
- }
- },
- .memory => |addr| {
- // The value is in memory at a hard-coded address.
- // If the type is a pointer, it means the pointer address is at this memory location.
- try self.genSetReg(src, reg, .{ .immediate = addr });
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, Instruction.Offset.none).toU32());
- },
- else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}),
- },
- .riscv64 => switch (mcv) {
- .dead => unreachable,
- .ptr_stack_offset => unreachable,
- .ptr_embedded_in_code => unreachable,
- .unreach, .none => return, // Nothing to do.
- .undef => {
- if (!self.wantSafety())
- return; // The already existing value will do just fine.
- // Write the debug undefined value.
- return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
- },
- .immediate => |unsigned_x| {
- const x = @bitCast(i64, unsigned_x);
- if (math.minInt(i12) <= x and x <= math.maxInt(i12)) {
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.addi(reg, .zero, @truncate(i12, x)).toU32());
- return;
- }
- if (math.minInt(i32) <= x and x <= math.maxInt(i32)) {
- const lo12 = @truncate(i12, x);
- const carry: i32 = if (lo12 < 0) 1 else 0;
- const hi20 = @truncate(i20, (x >> 12) +% carry);
-
- // TODO: add test case for 32-bit immediate
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.lui(reg, hi20).toU32());
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.addi(reg, reg, lo12).toU32());
- return;
- }
- // li rd, immediate
- // "Myriad sequences"
- return self.fail(src, "TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf
- },
- .memory => |addr| {
- // The value is in memory at a hard-coded address.
- // If the type is a pointer, it means the pointer address is at this memory location.
- try self.genSetReg(src, reg, .{ .immediate = addr });
-
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ld(reg, 0, reg).toU32());
- // LOAD imm=[i12 offset = 0], rs1 =
-
- // return self.fail("TODO implement genSetReg memory for riscv64");
- },
- else => return self.fail(src, "TODO implement getSetReg for riscv64 {}", .{mcv}),
- },
- .x86_64 => switch (mcv) {
- .dead => unreachable,
- .ptr_stack_offset => unreachable,
- .ptr_embedded_in_code => unreachable,
- .unreach, .none => return, // Nothing to do.
- .undef => {
- if (!self.wantSafety())
- return; // The already existing value will do just fine.
- // Write the debug undefined value.
- switch (reg.size()) {
- 8 => return self.genSetReg(src, reg, .{ .immediate = 0xaa }),
- 16 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaa }),
- 32 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }),
- 64 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
- else => unreachable,
- }
- },
- .compare_flags_unsigned => |op| {
- try self.code.ensureCapacity(self.code.items.len + 3);
- // TODO audit this codegen: we force w = true here to make
- // the value affect the big register
- self.rex(.{ .b = reg.isExtended(), .w = true });
- const opcode: u8 = switch (op) {
- .gte => 0x93,
- .gt => 0x97,
- .neq => 0x95,
- .lt => 0x92,
- .lte => 0x96,
- .eq => 0x94,
- };
- const id = @as(u8, reg.id() & 0b111);
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode, 0xC0 | id });
- },
- .compare_flags_signed => |op| {
- return self.fail(src, "TODO set register with compare flags value (signed)", .{});
- },
- .immediate => |x| {
- // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit
- // register is the fastest way to zero a register.
- if (x == 0) {
- // The encoding for `xor r32, r32` is `0x31 /r`.
- // Section 3.1.1.1 of the Intel x64 Manual states that "/r indicates that the
- // ModR/M byte of the instruction contains a register operand and an r/m operand."
- //
- // R/M bytes are composed of two bits for the mode, then three bits for the register,
- // then three bits for the operand. Since we're zeroing a register, the two three-bit
- // values will be identical, and the mode is three (the raw register value).
- //
- // If we're accessing e.g. r8d, we need to use a REX prefix before the actual operation. Since
- // this is a 32-bit operation, the W flag is set to zero. X is also zero, as we're not using a SIB.
- // Both R and B are set, as we're extending, in effect, the register bits *and* the operand.
- try self.code.ensureCapacity(self.code.items.len + 3);
- self.rex(.{ .r = reg.isExtended(), .b = reg.isExtended() });
- const id = @as(u8, reg.id() & 0b111);
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0x31, 0xC0 | id << 3 | id });
- return;
- }
- if (x <= math.maxInt(u32)) {
- // Next best case: if we set the lower four bytes, the upper four will be zeroed.
- //
- // The encoding for `mov IMM32 -> REG` is (0xB8 + R) IMM.
- if (reg.isExtended()) {
- // Just as with XORing, we need a REX prefix. This time though, we only
- // need the B bit set, as we're extending the opcode's register field,
- // and there is no Mod R/M byte.
- //
- // Thus, we need b01000001, or 0x41.
- try self.code.resize(self.code.items.len + 6);
- self.code.items[self.code.items.len - 6] = 0x41;
- } else {
- try self.code.resize(self.code.items.len + 5);
- }
- self.code.items[self.code.items.len - 5] = 0xB8 | @as(u8, reg.id() & 0b111);
- const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
- mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x));
- return;
- }
- // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls
- // this `movabs`, though this is officially just a different variant of the plain `mov`
- // instruction.
- //
- // This encoding is, in fact, the *same* as the one used for 32-bit loads. The only
- // difference is that we set REX.W before the instruction, which extends the load to
- // 64-bit and uses the full bit-width of the register.
- //
- // Since we always need a REX here, let's just check if we also need to set REX.B.
- //
- // In this case, the encoding of the REX byte is 0b0100100B
- try self.code.ensureCapacity(self.code.items.len + 10);
- self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() });
- self.code.items.len += 9;
- self.code.items[self.code.items.len - 9] = 0xB8 | @as(u8, reg.id() & 0b111);
- const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
- mem.writeIntLittle(u64, imm_ptr, x);
- },
- .embedded_in_code => |code_offset| {
- // We need the offset from RIP in a signed i32 twos complement.
- // The instruction is 7 bytes long and RIP points to the next instruction.
- try self.code.ensureCapacity(self.code.items.len + 7);
- // 64-bit LEA is encoded as REX.W 8D /r. If the register is extended, the REX byte is modified,
- // but the operation size is unchanged. Since we're using a disp32, we want mode 0 and lower three
- // bits as five.
- // REX 0x8D 0b00RRR101, where RRR is the lower three bits of the id.
- self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() });
- self.code.items.len += 6;
- const rip = self.code.items.len;
- const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip);
- const offset = @intCast(i32, big_offset);
- self.code.items[self.code.items.len - 6] = 0x8D;
- self.code.items[self.code.items.len - 5] = 0b101 | (@as(u8, reg.id() & 0b111) << 3);
- const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
- mem.writeIntLittle(i32, imm_ptr, offset);
- },
- .register => |src_reg| {
- // If the registers are the same, nothing to do.
- if (src_reg.id() == reg.id())
- return;
-
- // This is a variant of 8B /r. Since we're using 64-bit moves, we require a REX.
- // This is thus three bytes: REX 0x8B R/M.
- // If the destination is extended, the R field must be 1.
- // If the *source* is extended, the B field must be 1.
- // Since the register is being accessed directly, the R/M mode is three. The reg field (the middle
- // three bits) contain the destination, and the R/M field (the lower three bits) contain the source.
- try self.code.ensureCapacity(self.code.items.len + 3);
- self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended(), .b = src_reg.isExtended() });
- const R = 0xC0 | (@as(u8, reg.id() & 0b111) << 3) | @as(u8, src_reg.id() & 0b111);
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, R });
- },
- .memory => |x| {
- if (x <= math.maxInt(u32)) {
- // Moving from memory to a register is a variant of `8B /r`.
- // Since we're using 64-bit moves, we require a REX.
- // This variant also requires a SIB, as it would otherwise be RIP-relative.
- // We want mode zero with the lower three bits set to four to indicate an SIB with no other displacement.
- // The SIB must be 0x25, to indicate a disp32 with no scaled index.
- // 0b00RRR100, where RRR is the lower three bits of the register ID.
- // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32.
- try self.code.ensureCapacity(self.code.items.len + 8);
- self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() });
- self.code.appendSliceAssumeCapacity(&[_]u8{
- 0x8B,
- 0x04 | (@as(u8, reg.id() & 0b111) << 3), // R
- 0x25,
- });
- mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), @intCast(u32, x));
- } else {
- // If this is RAX, we can use a direct load; otherwise, we need to load the address, then indirectly load
- // the value.
- if (reg.id() == 0) {
- // REX.W 0xA1 moffs64*
- // moffs64* is a 64-bit offset "relative to segment base", which really just means the
- // absolute address for all practical purposes.
- try self.code.resize(self.code.items.len + 10);
- // REX.W == 0x48
- self.code.items[self.code.items.len - 10] = 0x48;
- self.code.items[self.code.items.len - 9] = 0xA1;
- const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
- mem.writeIntLittle(u64, imm_ptr, x);
- } else {
- // This requires two instructions; a move imm as used above, followed by an indirect load using the register
- // as the address and the register as the destination.
- //
- // This cannot be used if the lower three bits of the id are equal to four or five, as there
- // is no way to possibly encode it. This means that RSP, RBP, R12, and R13 cannot be used with
- // this instruction.
- const id3 = @truncate(u3, reg.id());
- assert(id3 != 4 and id3 != 5);
-
- // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.
- try self.genSetReg(src, reg, MCValue{ .immediate = x });
-
- // Now, the register contains the address of the value to load into it
- // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant.
- // TODO: determine whether to allow other sized registers, and if so, handle them properly.
- // This operation requires three bytes: REX 0x8B R/M
- try self.code.ensureCapacity(self.code.items.len + 3);
- // For this operation, we want R/M mode *zero* (use register indirectly), and the two register
- // values must match. Thus, it's 00ABCABC where ABC is the lower three bits of the register ID.
- //
- // Furthermore, if this is an extended register, both B and R must be set in the REX byte, as *both*
- // register operands need to be marked as extended.
- self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended(), .r = reg.isExtended() });
- const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id());
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, RM });
- }
- }
- },
- .stack_offset => |unadjusted_off| {
- try self.code.ensureCapacity(self.code.items.len + 7);
- const size_bytes = @divExact(reg.size(), 8);
- const off = unadjusted_off + size_bytes;
- self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended() });
- const reg_id: u8 = @truncate(u3, reg.id());
- if (off <= 128) {
- // Example: 48 8b 4d 7f mov rcx,QWORD PTR [rbp+0x7f]
- const RM = @as(u8, 0b01_000_101) | (reg_id << 3);
- const negative_offset = @intCast(i8, -@intCast(i32, off));
- const twos_comp = @bitCast(u8, negative_offset);
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8b, RM, twos_comp });
- } else if (off <= 2147483648) {
- // Example: 48 8b 8d 80 00 00 00 mov rcx,QWORD PTR [rbp+0x80]
- const RM = @as(u8, 0b10_000_101) | (reg_id << 3);
- const negative_offset = @intCast(i32, -@intCast(i33, off));
- const twos_comp = @bitCast(u32, negative_offset);
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8b, RM });
- mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), twos_comp);
- } else {
- return self.fail(src, "stack offset too large", .{});
- }
- },
- },
- else => return self.fail(src, "TODO implement getSetReg for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genPtrToInt(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- // no-op
- return self.resolveInst(inst.operand);
- }
-
- fn genBitCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- const operand = try self.resolveInst(inst.operand);
- return operand;
- }
-
- fn resolveInst(self: *Self, inst: *ir.Inst) !MCValue {
- // If the type has no codegen bits, no need to store it.
- if (!inst.ty.hasCodeGenBits())
- return MCValue.none;
-
- // Constants have static lifetimes, so they are always memoized in the outer most table.
- if (inst.castTag(.constant)) |const_inst| {
- const branch = &self.branch_stack.items[0];
- const gop = try branch.inst_table.getOrPut(self.gpa, inst);
- if (!gop.found_existing) {
- gop.entry.value = try self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
- }
- return gop.entry.value;
- }
-
- return self.getResolvedInstValue(inst);
- }
-
- fn getResolvedInstValue(self: *Self, inst: *ir.Inst) MCValue {
- // Treat each stack item as a "layer" on top of the previous one.
- var i: usize = self.branch_stack.items.len;
- while (true) {
- i -= 1;
- if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
- assert(mcv != .dead);
- return mcv;
- }
- }
- }
-
- /// If the MCValue is an immediate, and it does not fit within this type,
- /// we put it in a register.
- /// A potential opportunity for future optimization here would be keeping track
- /// of the fact that the instruction is available both as an immediate
- /// and as a register.
- fn limitImmediateType(self: *Self, inst: *ir.Inst, comptime T: type) !MCValue {
- const mcv = try self.resolveInst(inst);
- const ti = @typeInfo(T).Int;
- switch (mcv) {
- .immediate => |imm| {
- // This immediate is unsigned.
- const U = @Type(.{
- .Int = .{
- .bits = ti.bits - @boolToInt(ti.is_signed),
- .is_signed = false,
- },
- });
- if (imm >= math.maxInt(U)) {
- return MCValue{ .register = try self.copyToTmpRegister(inst.src, mcv) };
- }
- },
- else => {},
- }
- return mcv;
- }
-
- fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) InnerError!MCValue {
- if (typed_value.val.isUndef())
- return MCValue{ .undef = {} };
- const ptr_bits = self.target.cpu.arch.ptrBitWidth();
- const ptr_bytes: u64 = @divExact(ptr_bits, 8);
- switch (typed_value.ty.zigTypeTag()) {
- .Pointer => {
- if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
- if (self.bin_file.cast(link.File.Elf)) |elf_file| {
- const decl = payload.decl;
- const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
- const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
- return MCValue{ .memory = got_addr };
- } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
- const decl = payload.decl;
- const got = &macho_file.sections.items[macho_file.got_section_index.?];
- const got_addr = got.addr + decl.link.macho.offset_table_index.? * ptr_bytes;
- return MCValue{ .memory = got_addr };
- } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
- const decl = payload.decl;
- const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
- return MCValue{ .memory = got_addr };
- } else {
- return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{});
- }
- }
- return self.fail(src, "TODO codegen more kinds of const pointers", .{});
- },
- .Int => {
- const info = typed_value.ty.intInfo(self.target.*);
- if (info.bits > ptr_bits or info.signed) {
- return self.fail(src, "TODO const int bigger than ptr and signed int", .{});
- }
- return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
- },
- .Bool => {
- return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
- },
- .ComptimeInt => unreachable, // semantic analysis prevents this
- .ComptimeFloat => unreachable, // semantic analysis prevents this
- .Optional => {
- if (typed_value.ty.isPtrLikeOptional()) {
- if (typed_value.val.isNull())
- return MCValue{ .immediate = 0 };
-
- var buf: Type.Payload.PointerSimple = undefined;
- return self.genTypedValue(src, .{
- .ty = typed_value.ty.optionalChild(&buf),
- .val = typed_value.val,
- });
- } else if (typed_value.ty.abiSize(self.target.*) == 1) {
- return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };
- }
- return self.fail(src, "TODO non pointer optionals", .{});
- },
- else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),
- }
- }
-
- const CallMCValues = struct {
- args: []MCValue,
- return_value: MCValue,
- stack_byte_count: u32,
- stack_align: u32,
-
- fn deinit(self: *CallMCValues, func: *Self) void {
- func.gpa.free(self.args);
- self.* = undefined;
- }
- };
-
- /// Caller must call `CallMCValues.deinit`.
- fn resolveCallingConventionValues(self: *Self, src: usize, fn_ty: Type) !CallMCValues {
- const cc = fn_ty.fnCallingConvention();
- const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
- defer self.gpa.free(param_types);
- fn_ty.fnParamTypes(param_types);
- var result: CallMCValues = .{
- .args = try self.gpa.alloc(MCValue, param_types.len),
- // These undefined values must be populated before returning from this function.
- .return_value = undefined,
- .stack_byte_count = undefined,
- .stack_align = undefined,
- };
- errdefer self.gpa.free(result.args);
-
- const ret_ty = fn_ty.fnReturnType();
-
- switch (arch) {
- .x86_64 => {
- switch (cc) {
- .Naked => {
- assert(result.args.len == 0);
- result.return_value = .{ .unreach = {} };
- result.stack_byte_count = 0;
- result.stack_align = 1;
- return result;
- },
- .Unspecified, .C => {
- var next_int_reg: usize = 0;
- var next_stack_offset: u32 = 0;
-
- for (param_types) |ty, i| {
- switch (ty.zigTypeTag()) {
- .Bool, .Int => {
- const param_size = @intCast(u32, ty.abiSize(self.target.*));
- if (next_int_reg >= c_abi_int_param_regs.len) {
- result.args[i] = .{ .stack_offset = next_stack_offset };
- next_stack_offset += param_size;
- } else {
- const aliased_reg = registerAlias(
- c_abi_int_param_regs[next_int_reg],
- param_size,
- );
- result.args[i] = .{ .register = aliased_reg };
- next_int_reg += 1;
- }
- },
- else => return self.fail(src, "TODO implement function parameters of type {}", .{@tagName(ty.zigTypeTag())}),
- }
- }
- result.stack_byte_count = next_stack_offset;
- result.stack_align = 16;
- },
- else => return self.fail(src, "TODO implement function parameters for {} on x86_64", .{cc}),
- }
- },
- else => if (param_types.len != 0)
- return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}),
- }
-
- if (ret_ty.zigTypeTag() == .NoReturn) {
- result.return_value = .{ .unreach = {} };
- } else if (!ret_ty.hasCodeGenBits()) {
- result.return_value = .{ .none = {} };
- } else switch (arch) {
- .x86_64 => switch (cc) {
- .Naked => unreachable,
- .Unspecified, .C => {
- const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
- const aliased_reg = registerAlias(c_abi_int_return_regs[0], ret_ty_size);
- result.return_value = .{ .register = aliased_reg };
- },
- else => return self.fail(src, "TODO implement function return values for {}", .{cc}),
- },
- else => return self.fail(src, "TODO implement codegen return values for {}", .{self.target.cpu.arch}),
- }
- return result;
- }
-
- /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`.
- fn wantSafety(self: *Self) bool {
- return switch (self.bin_file.options.optimize_mode) {
- .Debug => true,
- .ReleaseSafe => true,
- .ReleaseFast => false,
- .ReleaseSmall => false,
- };
- }
-
- fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) InnerError {
- @setCold(true);
- assert(self.err_msg == null);
- self.err_msg = try ErrorMsg.create(self.bin_file.allocator, src, format, args);
- return error.CodegenFail;
- }
-
- usingnamespace switch (arch) {
- .i386 => @import("codegen/x86.zig"),
- .x86_64 => @import("codegen/x86_64.zig"),
- .riscv64 => @import("codegen/riscv64.zig"),
- .spu_2 => @import("codegen/spu-mk2.zig"),
- .arm => @import("codegen/arm.zig"),
- .armeb => @import("codegen/arm.zig"),
- else => struct {
- pub const Register = enum {
- dummy,
-
- pub fn allocIndex(self: Register) ?u4 {
- return null;
- }
- };
- pub const callee_preserved_regs = [_]Register{};
- },
- };
-
- /// An integer whose bits represent all the registers and whether they are free.
- const FreeRegInt = @Type(.{ .Int = .{ .is_signed = false, .bits = callee_preserved_regs.len } });
-
- fn parseRegName(name: []const u8) ?Register {
- if (@hasDecl(Register, "parseRegName")) {
- return Register.parseRegName(name);
- }
- return std.meta.stringToEnum(Register, name);
- }
-
- fn registerAlias(reg: Register, size_bytes: u32) Register {
- switch (arch) {
- // For x86_64 we have to pick a smaller register alias depending on abi size.
- .x86_64 => switch (size_bytes) {
- 1 => return reg.to8(),
- 2 => return reg.to16(),
- 4 => return reg.to32(),
- 8 => return reg.to64(),
- else => unreachable,
- },
- else => return reg,
- }
- }
-
- /// For most architectures this does nothing. For x86_64 it resolves any aliased registers
- /// to the 64-bit wide ones.
- fn toCanonicalReg(reg: Register) Register {
- return switch (arch) {
- .x86_64 => reg.to64(),
- else => reg,
- };
- }
- };
-}
diff --git a/src-self-hosted/codegen/arm.zig b/src-self-hosted/codegen/arm.zig
deleted file mode 100644
index 05178ea7d37afb551ea9baed8f1ba09cdad739fa..0000000000000000000000000000000000000000
--- a/src-self-hosted/codegen/arm.zig
+++ /dev/null
@@ -1,607 +0,0 @@
-const std = @import("std");
-const DW = std.dwarf;
-const testing = std.testing;
-
-/// The condition field specifies the flags neccessary for an
-/// Instruction to be executed
-pub const Condition = enum(u4) {
- /// equal
- eq,
- /// not equal
- ne,
- /// unsigned higher or same
- cs,
- /// unsigned lower
- cc,
- /// negative
- mi,
- /// positive or zero
- pl,
- /// overflow
- vs,
- /// no overflow
- vc,
- /// unsigned higer
- hi,
- /// unsigned lower or same
- ls,
- /// greater or equal
- ge,
- /// less than
- lt,
- /// greater than
- gt,
- /// less than or equal
- le,
- /// always
- al,
-};
-
-/// Represents a register in the ARM instruction set architecture
-pub const Register = enum(u5) {
- r0,
- r1,
- r2,
- r3,
- r4,
- r5,
- r6,
- r7,
- r8,
- r9,
- r10,
- r11,
- r12,
- r13,
- r14,
- r15,
-
- /// Argument / result / scratch register 1
- a1,
- /// Argument / result / scratch register 2
- a2,
- /// Argument / scratch register 3
- a3,
- /// Argument / scratch register 4
- a4,
- /// Variable-register 1
- v1,
- /// Variable-register 2
- v2,
- /// Variable-register 3
- v3,
- /// Variable-register 4
- v4,
- /// Variable-register 5
- v5,
- /// Platform register
- v6,
- /// Variable-register 7
- v7,
- /// Frame pointer or Variable-register 8
- fp,
- /// Intra-Procedure-call scratch register
- ip,
- /// Stack pointer
- sp,
- /// Link register
- lr,
- /// Program counter
- pc,
-
- /// Returns the unique 4-bit ID of this register which is used in
- /// the machine code
- pub fn id(self: Register) u4 {
- return @truncate(u4, @enumToInt(self));
- }
-
- /// Returns the index into `callee_preserved_regs`.
- pub fn allocIndex(self: Register) ?u4 {
- inline for (callee_preserved_regs) |cpreg, i| {
- if (self.id() == cpreg.id()) return i;
- }
- return null;
- }
-
- pub fn dwarfLocOp(self: Register) u8 {
- return @as(u8, self.id()) + DW.OP_reg0;
- }
-};
-
-test "Register.id" {
- testing.expectEqual(@as(u4, 15), Register.r15.id());
- testing.expectEqual(@as(u4, 15), Register.pc.id());
-}
-
-pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 };
-pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
-pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
-
-/// Represents an instruction in the ARM instruction set architecture
-pub const Instruction = union(enum) {
- DataProcessing: packed struct {
- // Note to self: The order of the fields top-to-bottom is
- // right-to-left in the actual 32-bit int representation
- op2: u12,
- rd: u4,
- rn: u4,
- s: u1,
- opcode: u4,
- i: u1,
- fixed: u2 = 0b00,
- cond: u4,
- },
- SingleDataTransfer: packed struct {
- offset: u12,
- rd: u4,
- rn: u4,
- l: u1,
- w: u1,
- b: u1,
- u: u1,
- p: u1,
- i: u1,
- fixed: u2 = 0b01,
- cond: u4,
- },
- Branch: packed struct {
- offset: u24,
- link: u1,
- fixed: u3 = 0b101,
- cond: u4,
- },
- BranchExchange: packed struct {
- rn: u4,
- fixed_1: u1 = 0b1,
- link: u1,
- fixed_2: u22 = 0b0001_0010_1111_1111_1111_00,
- cond: u4,
- },
- SupervisorCall: packed struct {
- comment: u24,
- fixed: u4 = 0b1111,
- cond: u4,
- },
- Breakpoint: packed struct {
- imm4: u4,
- fixed_1: u4 = 0b0111,
- imm12: u12,
- fixed_2_and_cond: u12 = 0b1110_0001_0010,
- },
-
- /// Represents the possible operations which can be performed by a
- /// DataProcessing instruction
- const Opcode = enum(u4) {
- // Rd := Op1 AND Op2
- @"and",
- // Rd := Op1 EOR Op2
- eor,
- // Rd := Op1 - Op2
- sub,
- // Rd := Op2 - Op1
- rsb,
- // Rd := Op1 + Op2
- add,
- // Rd := Op1 + Op2 + C
- adc,
- // Rd := Op1 - Op2 + C - 1
- sbc,
- // Rd := Op2 - Op1 + C - 1
- rsc,
- // set condition codes on Op1 AND Op2
- tst,
- // set condition codes on Op1 EOR Op2
- teq,
- // set condition codes on Op1 - Op2
- cmp,
- // set condition codes on Op1 + Op2
- cmn,
- // Rd := Op1 OR Op2
- orr,
- // Rd := Op2
- mov,
- // Rd := Op1 AND NOT Op2
- bic,
- // Rd := NOT Op2
- mvn,
- };
-
- /// Represents the second operand to a data processing instruction
- /// which can either be content from a register or an immediate
- /// value
- pub const Operand = union(enum) {
- Register: packed struct {
- rm: u4,
- shift: u8,
- },
- Immediate: packed struct {
- imm: u8,
- rotate: u4,
- },
-
- /// Represents multiple ways a register can be shifted. A
- /// register can be shifted by a specific immediate value or
- /// by the contents of another register
- pub const Shift = union(enum) {
- Immediate: packed struct {
- fixed: u1 = 0b0,
- typ: u2,
- amount: u5,
- },
- Register: packed struct {
- fixed_1: u1 = 0b1,
- typ: u2,
- fixed_2: u1 = 0b0,
- rs: u4,
- },
-
- const Type = enum(u2) {
- LogicalLeft,
- LogicalRight,
- ArithmeticRight,
- RotateRight,
- };
-
- const none = Shift{
- .Immediate = .{
- .amount = 0,
- .typ = 0,
- },
- };
-
- pub fn toU8(self: Shift) u8 {
- return switch (self) {
- .Register => |v| @bitCast(u8, v),
- .Immediate => |v| @bitCast(u8, v),
- };
- }
-
- pub fn reg(rs: Register, typ: Type) Shift {
- return Shift{
- .Register = .{
- .rs = rs.id(),
- .typ = @enumToInt(typ),
- },
- };
- }
-
- pub fn imm(amount: u5, typ: Type) Shift {
- return Shift{
- .Immediate = .{
- .amount = amount,
- .typ = @enumToInt(typ),
- },
- };
- }
- };
-
- pub fn toU12(self: Operand) u12 {
- return switch (self) {
- .Register => |v| @bitCast(u12, v),
- .Immediate => |v| @bitCast(u12, v),
- };
- }
-
- pub fn reg(rm: Register, shift: Shift) Operand {
- return Operand{
- .Register = .{
- .rm = rm.id(),
- .shift = shift.toU8(),
- },
- };
- }
-
- pub fn imm(immediate: u8, rotate: u4) Operand {
- return Operand{
- .Immediate = .{
- .imm = immediate,
- .rotate = rotate,
- },
- };
- }
- };
-
- /// Represents the offset operand of a load or store
- /// instruction. Data can be loaded from memory with either an
- /// immediate offset or an offset that is stored in some register.
- pub const Offset = union(enum) {
- Immediate: u12,
- Register: packed struct {
- rm: u4,
- shift: u8,
- },
-
- pub const none = Offset{
- .Immediate = 0,
- };
-
- pub fn toU12(self: Offset) u12 {
- return switch (self) {
- .Register => |v| @bitCast(u12, v),
- .Immediate => |v| v,
- };
- }
-
- pub fn reg(rm: Register, shift: u8) Offset {
- return Offset{
- .Register = .{
- .rm = rm.id(),
- .shift = shift,
- },
- };
- }
-
- pub fn imm(immediate: u8) Offset {
- return Offset{
- .Immediate = immediate,
- };
- }
- };
-
- pub fn toU32(self: Instruction) u32 {
- return switch (self) {
- .DataProcessing => |v| @bitCast(u32, v),
- .SingleDataTransfer => |v| @bitCast(u32, v),
- .Branch => |v| @bitCast(u32, v),
- .BranchExchange => |v| @bitCast(u32, v),
- .SupervisorCall => |v| @bitCast(u32, v),
- .Breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20),
- };
- }
-
- // Helper functions for the "real" functions below
-
- fn dataProcessing(
- cond: Condition,
- opcode: Opcode,
- s: u1,
- rd: Register,
- rn: Register,
- op2: Operand,
- ) Instruction {
- return Instruction{
- .DataProcessing = .{
- .cond = @enumToInt(cond),
- .i = if (op2 == .Immediate) 1 else 0,
- .opcode = @enumToInt(opcode),
- .s = s,
- .rn = rn.id(),
- .rd = rd.id(),
- .op2 = op2.toU12(),
- },
- };
- }
-
- fn singleDataTransfer(
- cond: Condition,
- rd: Register,
- rn: Register,
- offset: Offset,
- pre_post: u1,
- up_down: u1,
- byte_word: u1,
- writeback: u1,
- load_store: u1,
- ) Instruction {
- return Instruction{
- .SingleDataTransfer = .{
- .cond = @enumToInt(cond),
- .rn = rn.id(),
- .rd = rd.id(),
- .offset = offset.toU12(),
- .l = load_store,
- .w = writeback,
- .b = byte_word,
- .u = up_down,
- .p = pre_post,
- .i = if (offset == .Immediate) 0 else 1,
- },
- };
- }
-
- fn branch(cond: Condition, offset: i24, link: u1) Instruction {
- return Instruction{
- .Branch = .{
- .cond = @enumToInt(cond),
- .link = link,
- .offset = @bitCast(u24, offset),
- },
- };
- }
-
- fn branchExchange(cond: Condition, rn: Register, link: u1) Instruction {
- return Instruction{
- .BranchExchange = .{
- .cond = @enumToInt(cond),
- .link = link,
- .rn = rn.id(),
- },
- };
- }
-
- fn supervisorCall(cond: Condition, comment: u24) Instruction {
- return Instruction{
- .SupervisorCall = .{
- .cond = @enumToInt(cond),
- .comment = comment,
- },
- };
- }
-
- fn breakpoint(imm: u16) Instruction {
- return Instruction{
- .Breakpoint = .{
- .imm12 = @truncate(u12, imm >> 4),
- .imm4 = @truncate(u4, imm),
- },
- };
- }
-
- // Public functions replicating assembler syntax as closely as
- // possible
-
- // Data processing
-
- pub fn @"and"(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .@"and", s, rd, rn, op2);
- }
-
- pub fn eor(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .eor, s, rd, rn, op2);
- }
-
- pub fn sub(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .sub, s, rd, rn, op2);
- }
-
- pub fn rsb(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .rsb, s, rd, rn, op2);
- }
-
- pub fn add(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .add, s, rd, rn, op2);
- }
-
- pub fn adc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .adc, s, rd, rn, op2);
- }
-
- pub fn sbc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .sbc, s, rd, rn, op2);
- }
-
- pub fn rsc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .rsc, s, rd, rn, op2);
- }
-
- pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .tst, 1, .r0, rn, op2);
- }
-
- pub fn teq(cond: Condition, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .teq, 1, .r0, rn, op2);
- }
-
- pub fn cmp(cond: Condition, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .cmp, 1, .r0, rn, op2);
- }
-
- pub fn cmn(cond: Condition, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .cmn, 1, .r0, rn, op2);
- }
-
- pub fn orr(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .orr, s, rd, rn, op2);
- }
-
- pub fn mov(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .mov, s, rd, .r0, op2);
- }
-
- pub fn bic(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .bic, s, rd, rn, op2);
- }
-
- pub fn mvn(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .mvn, s, rd, .r0, op2);
- }
-
- // Single data transfer
-
- pub fn ldr(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction {
- return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 1);
- }
-
- pub fn str(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction {
- return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 0);
- }
-
- // Branch
-
- pub fn b(cond: Condition, offset: i24) Instruction {
- return branch(cond, offset, 0);
- }
-
- pub fn bl(cond: Condition, offset: i24) Instruction {
- return branch(cond, offset, 1);
- }
-
- // Branch and exchange
-
- pub fn bx(cond: Condition, rn: Register) Instruction {
- return branchExchange(cond, rn, 0);
- }
-
- pub fn blx(cond: Condition, rn: Register) Instruction {
- return branchExchange(cond, rn, 1);
- }
-
- // Supervisor Call
-
- pub const swi = svc;
-
- pub fn svc(cond: Condition, comment: u24) Instruction {
- return supervisorCall(cond, comment);
- }
-
- // Breakpoint
-
- pub fn bkpt(imm: u16) Instruction {
- return breakpoint(imm);
- }
-};
-
-test "serialize instructions" {
- const Testcase = struct {
- inst: Instruction,
- expected: u32,
- };
-
- const testcases = [_]Testcase{
- .{ // add r0, r0, r0
- .inst = Instruction.add(.al, 0, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)),
- .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000,
- },
- .{ // mov r4, r2
- .inst = Instruction.mov(.al, 0, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)),
- .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010,
- },
- .{ // mov r0, #42
- .inst = Instruction.mov(.al, 0, .r0, Instruction.Operand.imm(42, 0)),
- .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010,
- },
- .{ // ldr r0, [r2, #42]
- .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)),
- .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010,
- },
- .{ // str r0, [r3]
- .inst = Instruction.str(.al, .r0, .r3, Instruction.Offset.none),
- .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000,
- },
- .{ // b #12
- .inst = Instruction.b(.al, 12),
- .expected = 0b1110_101_0_0000_0000_0000_0000_0000_1100,
- },
- .{ // bl #-4
- .inst = Instruction.bl(.al, -4),
- .expected = 0b1110_101_1_1111_1111_1111_1111_1111_1100,
- },
- .{ // bx lr
- .inst = Instruction.bx(.al, .lr),
- .expected = 0b1110_0001_0010_1111_1111_1111_0001_1110,
- },
- .{ // svc #0
- .inst = Instruction.svc(.al, 0),
- .expected = 0b1110_1111_0000_0000_0000_0000_0000_0000,
- },
- .{ // bkpt #42
- .inst = Instruction.bkpt(42),
- .expected = 0b1110_0001_0010_000000000010_0111_1010,
- },
- };
-
- for (testcases) |case| {
- const actual = case.inst.toU32();
- testing.expectEqual(case.expected, actual);
- }
-}
diff --git a/src-self-hosted/codegen/c.zig b/src-self-hosted/codegen/c.zig
deleted file mode 100644
index 34ddcfbb3b33bf9925cd90234324821fc9d1fdc2..0000000000000000000000000000000000000000
--- a/src-self-hosted/codegen/c.zig
+++ /dev/null
@@ -1,299 +0,0 @@
-const std = @import("std");
-
-const link = @import("../link.zig");
-const Module = @import("../Module.zig");
-
-const Inst = @import("../ir.zig").Inst;
-const Value = @import("../value.zig").Value;
-const Type = @import("../type.zig").Type;
-
-const C = link.File.C;
-const Decl = Module.Decl;
-const mem = std.mem;
-
-/// Maps a name from Zig source to C. Currently, this will always give the same
-/// output for any given input, sometimes resulting in broken identifiers.
-fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {
- return allocator.dupe(u8, name);
-}
-
-fn renderType(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type) !void {
- switch (T.zigTypeTag()) {
- .NoReturn => {
- try writer.writeAll("zig_noreturn void");
- },
- .Void => try writer.writeAll("void"),
- .Int => {
- if (T.tag() == .u8) {
- ctx.file.need_stdint = true;
- try writer.writeAll("uint8_t");
- } else if (T.tag() == .usize) {
- ctx.file.need_stddef = true;
- try writer.writeAll("size_t");
- } else {
- return ctx.file.fail(ctx.decl.src(), "TODO implement int types", .{});
- }
- },
- else => |e| return ctx.file.fail(ctx.decl.src(), "TODO implement type {}", .{e}),
- }
-}
-
-fn renderValue(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type, val: Value) !void {
- switch (T.zigTypeTag()) {
- .Int => {
- if (T.isSignedInt())
- return writer.print("{}", .{val.toSignedInt()});
- return writer.print("{}", .{val.toUnsignedInt()});
- },
- else => |e| return ctx.file.fail(ctx.decl.src(), "TODO implement value {}", .{e}),
- }
-}
-
-fn renderFunctionSignature(ctx: *Context, writer: std.ArrayList(u8).Writer, decl: *Decl) !void {
- const tv = decl.typed_value.most_recent.typed_value;
- try renderType(ctx, writer, tv.ty.fnReturnType());
- const name = try map(ctx.file.base.allocator, mem.spanZ(decl.name));
- defer ctx.file.base.allocator.free(name);
- try writer.print(" {}(", .{name});
- var param_len = tv.ty.fnParamLen();
- if (param_len == 0)
- try writer.writeAll("void")
- else {
- var index: usize = 0;
- while (index < param_len) : (index += 1) {
- if (index > 0) {
- try writer.writeAll(", ");
- }
- try renderType(ctx, writer, tv.ty.fnParamType(index));
- try writer.print(" arg{}", .{index});
- }
- }
- try writer.writeByte(')');
-}
-
-pub fn generate(file: *C, decl: *Decl) !void {
- switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
- .Fn => try genFn(file, decl),
- .Array => try genArray(file, decl),
- else => |e| return file.fail(decl.src(), "TODO {}", .{e}),
- }
-}
-
-fn genArray(file: *C, decl: *Decl) !void {
- const tv = decl.typed_value.most_recent.typed_value;
- // TODO: prevent inline asm constants from being emitted
- const name = try map(file.base.allocator, mem.span(decl.name));
- defer file.base.allocator.free(name);
- if (tv.val.cast(Value.Payload.Bytes)) |payload|
- if (tv.ty.sentinel()) |sentinel|
- if (sentinel.toUnsignedInt() == 0)
- try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data })
- else
- return file.fail(decl.src(), "TODO byte arrays with non-zero sentinels", .{})
- else
- return file.fail(decl.src(), "TODO byte arrays without sentinels", .{})
- else
- return file.fail(decl.src(), "TODO non-byte arrays", .{});
-}
-
-const Context = struct {
- file: *C,
- decl: *Decl,
- inst_map: std.AutoHashMap(*Inst, []u8),
- argdex: usize = 0,
- unnamed_index: usize = 0,
-
- fn name(self: *Context) ![]u8 {
- const val = try std.fmt.allocPrint(self.file.base.allocator, "__temp_{}", .{self.unnamed_index});
- self.unnamed_index += 1;
- return val;
- }
-
- fn deinit(self: *Context) void {
- var it = self.inst_map.iterator();
- while (it.next()) |kv| {
- self.file.base.allocator.free(kv.value);
- }
- self.inst_map.deinit();
- self.* = undefined;
- }
-};
-
-fn genFn(file: *C, decl: *Decl) !void {
- const writer = file.main.writer();
- const tv = decl.typed_value.most_recent.typed_value;
-
- var ctx = Context{
- .file = file,
- .decl = decl,
- .inst_map = std.AutoHashMap(*Inst, []u8).init(file.base.allocator),
- };
- defer ctx.deinit();
-
- try renderFunctionSignature(&ctx, writer, decl);
-
- try writer.writeAll(" {");
-
- const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func;
- const instructions = func.analysis.success.instructions;
- if (instructions.len > 0) {
- try writer.writeAll("\n");
- for (instructions) |inst| {
- if (switch (inst.tag) {
- .assembly => try genAsm(&ctx, inst.castTag(.assembly).?),
- .call => try genCall(&ctx, inst.castTag(.call).?),
- .ret => try genRet(&ctx, inst.castTag(.ret).?),
- .retvoid => try genRetVoid(&ctx),
- .arg => try genArg(&ctx),
- .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?),
- .breakpoint => try genBreak(&ctx, inst.castTag(.breakpoint).?),
- .unreach => try genUnreach(&ctx, inst.castTag(.unreach).?),
- .intcast => try genIntCast(&ctx, inst.castTag(.intcast).?),
- else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),
- }) |name| {
- try ctx.inst_map.putNoClobber(inst, name);
- }
- }
- }
-
- try writer.writeAll("}\n\n");
-}
-
-fn genArg(ctx: *Context) !?[]u8 {
- const name = try std.fmt.allocPrint(ctx.file.base.allocator, "arg{}", .{ctx.argdex});
- ctx.argdex += 1;
- return name;
-}
-
-fn genRetVoid(ctx: *Context) !?[]u8 {
- try ctx.file.main.writer().print(" return;\n", .{});
- return null;
-}
-
-fn genRet(ctx: *Context, inst: *Inst.UnOp) !?[]u8 {
- return ctx.file.fail(ctx.decl.src(), "TODO return", .{});
-}
-
-fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 {
- if (inst.base.isUnused())
- return null;
- const op = inst.operand;
- const writer = ctx.file.main.writer();
- const name = try ctx.name();
- const from = ctx.inst_map.get(op) orelse
- return ctx.file.fail(ctx.decl.src(), "Internal error in C backend: intCast argument not found in inst_map", .{});
- try writer.writeAll(" const ");
- try renderType(ctx, writer, inst.base.ty);
- try writer.print(" {} = (", .{name});
- try renderType(ctx, writer, inst.base.ty);
- try writer.print("){};\n", .{from});
- return name;
-}
-
-fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 {
- const writer = ctx.file.main.writer();
- const header = ctx.file.header.writer();
- try writer.writeAll(" ");
- if (inst.func.castTag(.constant)) |func_inst| {
- if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
- const target = func_val.func.owner_decl;
- const target_ty = target.typed_value.most_recent.typed_value.ty;
- const ret_ty = target_ty.fnReturnType().tag();
- if (target_ty.fnReturnType().hasCodeGenBits() and inst.base.isUnused()) {
- try writer.print("(void)", .{});
- }
- const tname = mem.spanZ(target.name);
- if (ctx.file.called.get(tname) == null) {
- try ctx.file.called.put(tname, void{});
- try renderFunctionSignature(ctx, header, target);
- try header.writeAll(";\n");
- }
- try writer.print("{}(", .{tname});
- if (inst.args.len != 0) {
- for (inst.args) |arg, i| {
- if (i > 0) {
- try writer.writeAll(", ");
- }
- if (arg.cast(Inst.Constant)) |con| {
- try renderValue(ctx, writer, arg.ty, con.val);
- } else {
- return ctx.file.fail(ctx.decl.src(), "TODO call pass arg {}", .{arg});
- }
- }
- }
- try writer.writeAll(");\n");
- } else {
- return ctx.file.fail(ctx.decl.src(), "TODO non-function call target?", .{});
- }
- } else {
- return ctx.file.fail(ctx.decl.src(), "TODO non-constant call inst?", .{});
- }
- return null;
-}
-
-fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
- // TODO emit #line directive here with line number and filename
- return null;
-}
-
-fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
- // TODO ??
- return null;
-}
-
-fn genUnreach(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
- try ctx.file.main.writer().writeAll(" zig_unreachable();\n");
- return null;
-}
-
-fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 {
- const writer = ctx.file.main.writer();
- try writer.writeAll(" ");
- for (as.inputs) |i, index| {
- if (i[0] == '{' and i[i.len - 1] == '}') {
- const reg = i[1 .. i.len - 1];
- const arg = as.args[index];
- try writer.writeAll("register ");
- try renderType(ctx, writer, arg.ty);
- try writer.print(" {}_constant __asm__(\"{}\") = ", .{ reg, reg });
- // TODO merge constant handling into inst_map as well
- if (arg.castTag(.constant)) |c| {
- try renderValue(ctx, writer, arg.ty, c.val);
- try writer.writeAll(";\n ");
- } else {
- const gop = try ctx.inst_map.getOrPut(arg);
- if (!gop.found_existing) {
- return ctx.file.fail(ctx.decl.src(), "Internal error in C backend: asm argument not found in inst_map", .{});
- }
- try writer.print("{};\n ", .{gop.entry.value});
- }
- } else {
- return ctx.file.fail(ctx.decl.src(), "TODO non-explicit inline asm regs", .{});
- }
- }
- try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source });
- if (as.output) |o| {
- return ctx.file.fail(ctx.decl.src(), "TODO inline asm output", .{});
- }
- if (as.inputs.len > 0) {
- if (as.output == null) {
- try writer.writeAll(" :");
- }
- try writer.writeAll(": ");
- for (as.inputs) |i, index| {
- if (i[0] == '{' and i[i.len - 1] == '}') {
- const reg = i[1 .. i.len - 1];
- const arg = as.args[index];
- if (index > 0) {
- try writer.writeAll(", ");
- }
- try writer.print("\"\"({}_constant)", .{reg});
- } else {
- // This is blocked by the earlier test
- unreachable;
- }
- }
- }
- try writer.writeAll(");\n");
- return null;
-}
diff --git a/src-self-hosted/codegen/riscv64.zig b/src-self-hosted/codegen/riscv64.zig
deleted file mode 100644
index 96b9c58f9c3b041263e44cd215ae5a71df63aa37..0000000000000000000000000000000000000000
--- a/src-self-hosted/codegen/riscv64.zig
+++ /dev/null
@@ -1,433 +0,0 @@
-const std = @import("std");
-const DW = std.dwarf;
-
-// TODO: this is only tagged to facilitate the monstrosity.
-// Once packed structs work make it packed.
-pub const Instruction = union(enum) {
- R: packed struct {
- opcode: u7,
- rd: u5,
- funct3: u3,
- rs1: u5,
- rs2: u5,
- funct7: u7,
- },
- I: packed struct {
- opcode: u7,
- rd: u5,
- funct3: u3,
- rs1: u5,
- imm0_11: u12,
- },
- S: packed struct {
- opcode: u7,
- imm0_4: u5,
- funct3: u3,
- rs1: u5,
- rs2: u5,
- imm5_11: u7,
- },
- B: packed struct {
- opcode: u7,
- imm11: u1,
- imm1_4: u4,
- funct3: u3,
- rs1: u5,
- rs2: u5,
- imm5_10: u6,
- imm12: u1,
- },
- U: packed struct {
- opcode: u7,
- rd: u5,
- imm12_31: u20,
- },
- J: packed struct {
- opcode: u7,
- rd: u5,
- imm12_19: u8,
- imm11: u1,
- imm1_10: u10,
- imm20: u1,
- },
-
- // TODO: once packed structs work we can remove this monstrosity.
- pub fn toU32(self: Instruction) u32 {
- return switch (self) {
- .R => |v| @bitCast(u32, v),
- .I => |v| @bitCast(u32, v),
- .S => |v| @bitCast(u32, v),
- .B => |v| @intCast(u32, v.opcode) + (@intCast(u32, v.imm11) << 7) + (@intCast(u32, v.imm1_4) << 8) + (@intCast(u32, v.funct3) << 12) + (@intCast(u32, v.rs1) << 15) + (@intCast(u32, v.rs2) << 20) + (@intCast(u32, v.imm5_10) << 25) + (@intCast(u32, v.imm12) << 31),
- .U => |v| @bitCast(u32, v),
- .J => |v| @bitCast(u32, v),
- };
- }
-
- fn rType(op: u7, fn3: u3, fn7: u7, rd: Register, r1: Register, r2: Register) Instruction {
- return Instruction{
- .R = .{
- .opcode = op,
- .funct3 = fn3,
- .funct7 = fn7,
- .rd = @enumToInt(rd),
- .rs1 = @enumToInt(r1),
- .rs2 = @enumToInt(r2),
- },
- };
- }
-
- // RISC-V is all signed all the time -- convert immediates to unsigned for processing
- fn iType(op: u7, fn3: u3, rd: Register, r1: Register, imm: i12) Instruction {
- const umm = @bitCast(u12, imm);
-
- return Instruction{
- .I = .{
- .opcode = op,
- .funct3 = fn3,
- .rd = @enumToInt(rd),
- .rs1 = @enumToInt(r1),
- .imm0_11 = umm,
- },
- };
- }
-
- fn sType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i12) Instruction {
- const umm = @bitCast(u12, imm);
-
- return Instruction{
- .S = .{
- .opcode = op,
- .funct3 = fn3,
- .rs1 = @enumToInt(r1),
- .rs2 = @enumToInt(r2),
- .imm0_4 = @truncate(u5, umm),
- .imm5_11 = @truncate(u7, umm >> 5),
- },
- };
- }
-
- // Use significance value rather than bit value, same for J-type
- // -- less burden on callsite, bonus semantic checking
- fn bType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i13) Instruction {
- const umm = @bitCast(u13, imm);
- if (umm % 2 != 0) @panic("Internal error: misaligned branch target");
-
- return Instruction{
- .B = .{
- .opcode = op,
- .funct3 = fn3,
- .rs1 = @enumToInt(r1),
- .rs2 = @enumToInt(r2),
- .imm1_4 = @truncate(u4, umm >> 1),
- .imm5_10 = @truncate(u6, umm >> 5),
- .imm11 = @truncate(u1, umm >> 11),
- .imm12 = @truncate(u1, umm >> 12),
- },
- };
- }
-
- // We have to extract the 20 bits anyway -- let's not make it more painful
- fn uType(op: u7, rd: Register, imm: i20) Instruction {
- const umm = @bitCast(u20, imm);
-
- return Instruction{
- .U = .{
- .opcode = op,
- .rd = @enumToInt(rd),
- .imm12_31 = umm,
- },
- };
- }
-
- fn jType(op: u7, rd: Register, imm: i21) Instruction {
- const umm = @bitcast(u21, imm);
- if (umm % 2 != 0) @panic("Internal error: misaligned jump target");
-
- return Instruction{
- .J = .{
- .opcode = op,
- .rd = @enumToInt(rd),
- .imm1_10 = @truncate(u10, umm >> 1),
- .imm11 = @truncate(u1, umm >> 1),
- .imm12_19 = @truncate(u8, umm >> 12),
- .imm20 = @truncate(u1, umm >> 20),
- },
- };
- }
-
- // The meat and potatoes. Arguments are in the order in which they would appear in assembly code.
-
- // Arithmetic/Logical, Register-Register
-
- pub fn add(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0110011, 0b000, 0b0000000, rd, r1, r2);
- }
-
- pub fn sub(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0110011, 0b000, 0b0100000, rd, r1, r2);
- }
-
- pub fn @"and"(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0110011, 0b111, 0b0000000, rd, r1, r2);
- }
-
- pub fn @"or"(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0110011, 0b110, 0b0000000, rd, r1, r2);
- }
-
- pub fn xor(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0110011, 0b100, 0b0000000, rd, r1, r2);
- }
-
- pub fn sll(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0110011, 0b001, 0b0000000, rd, r1, r2);
- }
-
- pub fn srl(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0110011, 0b101, 0b0000000, rd, r1, r2);
- }
-
- pub fn sra(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0110011, 0b101, 0b0100000, rd, r1, r2);
- }
-
- pub fn slt(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0110011, 0b010, 0b0000000, rd, r1, r2);
- }
-
- pub fn sltu(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0110011, 0b011, 0b0000000, rd, r1, r2);
- }
-
- // Arithmetic/Logical, Register-Register (32-bit)
-
- pub fn addw(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0111011, 0b000, rd, r1, r2);
- }
-
- pub fn subw(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0111011, 0b000, 0b0100000, rd, r1, r2);
- }
-
- pub fn sllw(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0111011, 0b001, 0b0000000, rd, r1, r2);
- }
-
- pub fn srlw(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0111011, 0b101, 0b0000000, rd, r1, r2);
- }
-
- pub fn sraw(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0111011, 0b101, 0b0100000, rd, r1, r2);
- }
-
- // Arithmetic/Logical, Register-Immediate
-
- pub fn addi(rd: Register, r1: Register, imm: i12) Instruction {
- return iType(0b0010011, 0b000, rd, r1, imm);
- }
-
- pub fn andi(rd: Register, r1: Register, imm: i12) Instruction {
- return iType(0b0010011, 0b111, rd, r1, imm);
- }
-
- pub fn ori(rd: Register, r1: Register, imm: i12) Instruction {
- return iType(0b0010011, 0b110, rd, r1, imm);
- }
-
- pub fn xori(rd: Register, r1: Register, imm: i12) Instruction {
- return iType(0b0010011, 0b100, rd, r1, imm);
- }
-
- pub fn slli(rd: Register, r1: Register, shamt: u6) Instruction {
- return iType(0b0010011, 0b001, rd, r1, shamt);
- }
-
- pub fn srli(rd: Register, r1: Register, shamt: u6) Instruction {
- return iType(0b0010011, 0b101, rd, r1, shamt);
- }
-
- pub fn srai(rd: Register, r1: Register, shamt: u6) Instruction {
- return iType(0b0010011, 0b101, rd, r1, (1 << 10) + shamt);
- }
-
- pub fn slti(rd: Register, r1: Register, imm: i12) Instruction {
- return iType(0b0010011, 0b010, rd, r1, imm);
- }
-
- pub fn sltiu(rd: Register, r1: Register, imm: u12) Instruction {
- return iType(0b0010011, 0b011, rd, r1, @bitCast(i12, imm));
- }
-
- // Arithmetic/Logical, Register-Immediate (32-bit)
-
- pub fn addiw(rd: Register, r1: Register, imm: i12) Instruction {
- return iType(0b0011011, 0b000, rd, r1, imm);
- }
-
- pub fn slliw(rd: Register, r1: Register, shamt: u5) Instruction {
- return iType(0b0011011, 0b001, rd, r1, shamt);
- }
-
- pub fn srliw(rd: Register, r1: Register, shamt: u5) Instruction {
- return iType(0b0011011, 0b101, rd, r1, shamt);
- }
-
- pub fn sraiw(rd: Register, r1: Register, shamt: u5) Instruction {
- return iType(0b0011011, 0b101, rd, r1, (1 << 10) + shamt);
- }
-
- // Upper Immediate
-
- pub fn lui(rd: Register, imm: i20) Instruction {
- return uType(0b0110111, rd, imm);
- }
-
- pub fn auipc(rd: Register, imm: i20) Instruction {
- return uType(0b0010111, rd, imm);
- }
-
- // Load
-
- pub fn ld(rd: Register, offset: i12, base: Register) Instruction {
- return iType(0b0000011, 0b011, rd, base, offset);
- }
-
- pub fn lw(rd: Register, offset: i12, base: Register) Instruction {
- return iType(0b0000011, 0b010, rd, base, offset);
- }
-
- pub fn lwu(rd: Register, offset: i12, base: Register) Instruction {
- return iType(0b0000011, 0b110, rd, base, offset);
- }
-
- pub fn lh(rd: Register, offset: i12, base: Register) Instruction {
- return iType(0b0000011, 0b001, rd, base, offset);
- }
-
- pub fn lhu(rd: Register, offset: i12, base: Register) Instruction {
- return iType(0b0000011, 0b101, rd, base, offset);
- }
-
- pub fn lb(rd: Register, offset: i12, base: Register) Instruction {
- return iType(0b0000011, 0b000, rd, base, offset);
- }
-
- pub fn lbu(rd: Register, offset: i12, base: Register) Instruction {
- return iType(0b0000011, 0b100, rd, base, offset);
- }
-
- // Store
-
- pub fn sd(rs: Register, offset: i12, base: Register) Instruction {
- return sType(0b0100011, 0b011, base, rs, offset);
- }
-
- pub fn sw(rs: Register, offset: i12, base: Register) Instruction {
- return sType(0b0100011, 0b010, base, rs, offset);
- }
-
- pub fn sh(rs: Register, offset: i12, base: Register) Instruction {
- return sType(0b0100011, 0b001, base, rs, offset);
- }
-
- pub fn sb(rs: Register, offset: i12, base: Register) Instruction {
- return sType(0b0100011, 0b000, base, rs, offset);
- }
-
- // Fence
- // TODO: implement fence
-
- // Branch
-
- pub fn beq(r1: Register, r2: Register, offset: u13) Instruction {
- return bType(0b1100011, 0b000, r1, r2, offset);
- }
-
- pub fn bne(r1: Register, r2: Register, offset: u13) Instruction {
- return bType(0b1100011, 0b001, r1, r2, offset);
- }
-
- pub fn blt(r1: Register, r2: Register, offset: u13) Instruction {
- return bType(0b1100011, 0b100, r1, r2, offset);
- }
-
- pub fn bge(r1: Register, r2: Register, offset: u13) Instruction {
- return bType(0b1100011, 0b101, r1, r2, offset);
- }
-
- pub fn bltu(r1: Register, r2: Register, offset: u13) Instruction {
- return bType(0b1100011, 0b110, r1, r2, offset);
- }
-
- pub fn bgeu(r1: Register, r2: Register, offset: u13) Instruction {
- return bType(0b1100011, 0b111, r1, r2, offset);
- }
-
- // Jump
-
- pub fn jal(link: Register, offset: i21) Instruction {
- return jType(0b1101111, link, offset);
- }
-
- pub fn jalr(link: Register, offset: i12, base: Register) Instruction {
- return iType(0b1100111, 0b000, link, base, offset);
- }
-
- // System
-
- pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000);
- pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001);
-};
-
-// zig fmt: off
-pub const RawRegister = enum(u5) {
- x0, x1, x2, x3, x4, x5, x6, x7,
- x8, x9, x10, x11, x12, x13, x14, x15,
- x16, x17, x18, x19, x20, x21, x22, x23,
- x24, x25, x26, x27, x28, x29, x30, x31,
-
- pub fn dwarfLocOp(reg: RawRegister) u8 {
- return @enumToInt(reg) + DW.OP_reg0;
- }
-};
-
-pub const Register = enum(u5) {
- // 64 bit registers
- zero, // zero
- ra, // return address. caller saved
- sp, // stack pointer. callee saved.
- gp, // global pointer
- tp, // thread pointer
- t0, t1, t2, // temporaries. caller saved.
- s0, // s0/fp, callee saved.
- s1, // callee saved.
- a0, a1, // fn args/return values. caller saved.
- a2, a3, a4, a5, a6, a7, // fn args. caller saved.
- s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, // saved registers. callee saved.
- t3, t4, t5, t6, // caller saved
-
- pub fn parseRegName(name: []const u8) ?Register {
- if(std.meta.stringToEnum(Register, name)) |reg| return reg;
- if(std.meta.stringToEnum(RawRegister, name)) |rawreg| return @intToEnum(Register, @enumToInt(rawreg));
- return null;
- }
-
- /// Returns the index into `callee_preserved_regs`.
- pub fn allocIndex(self: Register) ?u4 {
- inline for(callee_preserved_regs) |cpreg, i| {
- if(self == cpreg) return i;
- }
- return null;
- }
-
- pub fn dwarfLocOp(reg: Register) u8 {
- return @as(u8, @enumToInt(reg)) + DW.OP_reg0;
- }
-};
-
-// zig fmt: on
-
-pub const callee_preserved_regs = [_]Register{
- .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
-};
diff --git a/src-self-hosted/codegen/spu-mk2.zig b/src-self-hosted/codegen/spu-mk2.zig
deleted file mode 100644
index 542862cacaa444033616d002c8fcfb2133fea1a0..0000000000000000000000000000000000000000
--- a/src-self-hosted/codegen/spu-mk2.zig
+++ /dev/null
@@ -1,170 +0,0 @@
-const std = @import("std");
-
-pub const Interpreter = @import("spu-mk2/interpreter.zig").Interpreter;
-
-pub const ExecutionCondition = enum(u3) {
- always = 0,
- when_zero = 1,
- not_zero = 2,
- greater_zero = 3,
- less_than_zero = 4,
- greater_or_equal_zero = 5,
- less_or_equal_zero = 6,
- overflow = 7,
-};
-
-pub const InputBehaviour = enum(u2) {
- zero = 0,
- immediate = 1,
- peek = 2,
- pop = 3,
-};
-
-pub const OutputBehaviour = enum(u2) {
- discard = 0,
- push = 1,
- jump = 2,
- jump_relative = 3,
-};
-
-pub const Command = enum(u5) {
- copy = 0,
- ipget = 1,
- get = 2,
- set = 3,
- store8 = 4,
- store16 = 5,
- load8 = 6,
- load16 = 7,
- undefined0 = 8,
- undefined1 = 9,
- frget = 10,
- frset = 11,
- bpget = 12,
- bpset = 13,
- spget = 14,
- spset = 15,
- add = 16,
- sub = 17,
- mul = 18,
- div = 19,
- mod = 20,
- @"and" = 21,
- @"or" = 22,
- xor = 23,
- not = 24,
- signext = 25,
- rol = 26,
- ror = 27,
- bswap = 28,
- asr = 29,
- lsl = 30,
- lsr = 31,
-};
-
-pub const Instruction = packed struct {
- condition: ExecutionCondition,
- input0: InputBehaviour,
- input1: InputBehaviour,
- modify_flags: bool,
- output: OutputBehaviour,
- command: Command,
- reserved: u1 = 0,
-
- pub fn format(instr: Instruction, comptime fmt: []const u8, options: std.fmt.FormatOptions, out: anytype) !void {
- try std.fmt.format(out, "0x{x:0<4} ", .{@bitCast(u16, instr)});
- try out.writeAll(switch (instr.condition) {
- .always => " ",
- .when_zero => "== 0",
- .not_zero => "!= 0",
- .greater_zero => " > 0",
- .less_than_zero => " < 0",
- .greater_or_equal_zero => ">= 0",
- .less_or_equal_zero => "<= 0",
- .overflow => "ovfl",
- });
- try out.writeAll(" ");
- try out.writeAll(switch (instr.input0) {
- .zero => "zero",
- .immediate => "imm ",
- .peek => "peek",
- .pop => "pop ",
- });
- try out.writeAll(" ");
- try out.writeAll(switch (instr.input1) {
- .zero => "zero",
- .immediate => "imm ",
- .peek => "peek",
- .pop => "pop ",
- });
- try out.writeAll(" ");
- try out.writeAll(switch (instr.command) {
- .copy => "copy ",
- .ipget => "ipget ",
- .get => "get ",
- .set => "set ",
- .store8 => "store8 ",
- .store16 => "store16 ",
- .load8 => "load8 ",
- .load16 => "load16 ",
- .undefined0 => "undefined",
- .undefined1 => "undefined",
- .frget => "frget ",
- .frset => "frset ",
- .bpget => "bpget ",
- .bpset => "bpset ",
- .spget => "spget ",
- .spset => "spset ",
- .add => "add ",
- .sub => "sub ",
- .mul => "mul ",
- .div => "div ",
- .mod => "mod ",
- .@"and" => "and ",
- .@"or" => "or ",
- .xor => "xor ",
- .not => "not ",
- .signext => "signext ",
- .rol => "rol ",
- .ror => "ror ",
- .bswap => "bswap ",
- .asr => "asr ",
- .lsl => "lsl ",
- .lsr => "lsr ",
- });
- try out.writeAll(" ");
- try out.writeAll(switch (instr.output) {
- .discard => "discard",
- .push => "push ",
- .jump => "jmp ",
- .jump_relative => "rjmp ",
- });
- try out.writeAll(" ");
- try out.writeAll(if (instr.modify_flags)
- "+ flags"
- else
- " ");
- }
-};
-
-pub const FlagRegister = packed struct {
- zero: bool,
- negative: bool,
- carry: bool,
- carry_enabled: bool,
- interrupt0_enabled: bool,
- interrupt1_enabled: bool,
- interrupt2_enabled: bool,
- interrupt3_enabled: bool,
- reserved: u8 = 0,
-};
-
-pub const Register = enum {
- dummy,
-
- pub fn allocIndex(self: Register) ?u4 {
- return null;
- }
-};
-
-pub const callee_preserved_regs = [_]Register{};
diff --git a/src-self-hosted/codegen/spu-mk2/interpreter.zig b/src-self-hosted/codegen/spu-mk2/interpreter.zig
deleted file mode 100644
index 1ec99546c6cbe1ee30d1473e455824d2f6dc9421..0000000000000000000000000000000000000000
--- a/src-self-hosted/codegen/spu-mk2/interpreter.zig
+++ /dev/null
@@ -1,166 +0,0 @@
-const std = @import("std");
-const log = std.log.scoped(.SPU_2_Interpreter);
-const spu = @import("../spu-mk2.zig");
-const FlagRegister = spu.FlagRegister;
-const Instruction = spu.Instruction;
-const ExecutionCondition = spu.ExecutionCondition;
-
-pub fn Interpreter(comptime Bus: type) type {
- return struct {
- ip: u16 = 0,
- sp: u16 = undefined,
- bp: u16 = undefined,
- fr: FlagRegister = @bitCast(FlagRegister, @as(u16, 0)),
- /// This is set to true when we hit an undefined0 instruction, allowing it to
- /// be used as a trap for testing purposes
- undefined0: bool = false,
- /// This is set to true when we hit an undefined1 instruction, allowing it to
- /// be used as a trap for testing purposes. undefined1 is used as a breakpoint.
- undefined1: bool = false,
- bus: Bus,
-
- pub fn ExecuteBlock(self: *@This(), comptime size: ?u32) !void {
- var count: usize = 0;
- while (size == null or count < size.?) {
- count += 1;
- var instruction = @bitCast(Instruction, self.bus.read16(self.ip));
-
- log.debug("Executing {}\n", .{instruction});
-
- self.ip +%= 2;
-
- const execute = switch (instruction.condition) {
- .always => true,
- .not_zero => !self.fr.zero,
- .when_zero => self.fr.zero,
- .overflow => self.fr.carry,
- ExecutionCondition.greater_or_equal_zero => !self.fr.negative,
- else => return error.Unimplemented,
- };
-
- if (execute) {
- const val0 = switch (instruction.input0) {
- .zero => @as(u16, 0),
- .immediate => i: {
- const val = self.bus.read16(@intCast(u16, self.ip));
- self.ip +%= 2;
- break :i val;
- },
- else => |e| e: {
- // peek or pop; show value at current SP, and if pop, increment sp
- const val = self.bus.read16(self.sp);
- if (e == .pop) {
- self.sp +%= 2;
- }
- break :e val;
- },
- };
- const val1 = switch (instruction.input1) {
- .zero => @as(u16, 0),
- .immediate => i: {
- const val = self.bus.read16(@intCast(u16, self.ip));
- self.ip +%= 2;
- break :i val;
- },
- else => |e| e: {
- // peek or pop; show value at current SP, and if pop, increment sp
- const val = self.bus.read16(self.sp);
- if (e == .pop) {
- self.sp +%= 2;
- }
- break :e val;
- },
- };
-
- const output: u16 = switch (instruction.command) {
- .get => self.bus.read16(self.bp +% (2 *% val0)),
- .set => a: {
- self.bus.write16(self.bp +% 2 *% val0, val1);
- break :a val1;
- },
- .load8 => self.bus.read8(val0),
- .load16 => self.bus.read16(val0),
- .store8 => a: {
- const val = @truncate(u8, val1);
- self.bus.write8(val0, val);
- break :a val;
- },
- .store16 => a: {
- self.bus.write16(val0, val1);
- break :a val1;
- },
- .copy => val0,
- .add => a: {
- var val: u16 = undefined;
- self.fr.carry = @addWithOverflow(u16, val0, val1, &val);
- break :a val;
- },
- .sub => a: {
- var val: u16 = undefined;
- self.fr.carry = @subWithOverflow(u16, val0, val1, &val);
- break :a val;
- },
- .spset => a: {
- self.sp = val0;
- break :a val0;
- },
- .bpset => a: {
- self.bp = val0;
- break :a val0;
- },
- .frset => a: {
- const val = (@bitCast(u16, self.fr) & val1) | (val0 & ~val1);
- self.fr = @bitCast(FlagRegister, val);
- break :a val;
- },
- .bswap => (val0 >> 8) | (val0 << 8),
- .bpget => self.bp,
- .spget => self.sp,
- .ipget => self.ip +% (2 *% val0),
- .lsl => val0 << 1,
- .lsr => val0 >> 1,
- .@"and" => val0 & val1,
- .@"or" => val0 | val1,
- .xor => val0 ^ val1,
- .not => ~val0,
- .undefined0 => {
- self.undefined0 = true;
- // Break out of the loop, and let the caller decide what to do
- return;
- },
- .undefined1 => {
- self.undefined1 = true;
- // Break out of the loop, and let the caller decide what to do
- return;
- },
- .signext => if ((val0 & 0x80) != 0)
- (val0 & 0xFF) | 0xFF00
- else
- (val0 & 0xFF),
- else => return error.Unimplemented,
- };
-
- switch (instruction.output) {
- .discard => {},
- .push => {
- self.sp -%= 2;
- self.bus.write16(self.sp, output);
- },
- .jump => {
- self.ip = output;
- },
- else => return error.Unimplemented,
- }
- if (instruction.modify_flags) {
- self.fr.negative = (output & 0x8000) != 0;
- self.fr.zero = (output == 0x0000);
- }
- } else {
- if (instruction.input0 == .immediate) self.ip +%= 2;
- if (instruction.input1 == .immediate) self.ip +%= 2;
- break;
- }
- }
- }
- };
-}
diff --git a/src-self-hosted/codegen/wasm.zig b/src-self-hosted/codegen/wasm.zig
deleted file mode 100644
index e55e9049346c989e2653e6ba108f43966520541c..0000000000000000000000000000000000000000
--- a/src-self-hosted/codegen/wasm.zig
+++ /dev/null
@@ -1,141 +0,0 @@
-const std = @import("std");
-const Allocator = std.mem.Allocator;
-const ArrayList = std.ArrayList;
-const assert = std.debug.assert;
-const leb = std.debug.leb;
-const mem = std.mem;
-
-const Decl = @import("../Module.zig").Decl;
-const Inst = @import("../ir.zig").Inst;
-const Type = @import("../type.zig").Type;
-const Value = @import("../value.zig").Value;
-
-fn genValtype(ty: Type) u8 {
- return switch (ty.tag()) {
- .u32, .i32 => 0x7F,
- .u64, .i64 => 0x7E,
- .f32 => 0x7D,
- .f64 => 0x7C,
- else => @panic("TODO: Implement more types for wasm."),
- };
-}
-
-pub fn genFunctype(buf: *ArrayList(u8), decl: *Decl) !void {
- const ty = decl.typed_value.most_recent.typed_value.ty;
- const writer = buf.writer();
-
- // functype magic
- try writer.writeByte(0x60);
-
- // param types
- try leb.writeULEB128(writer, @intCast(u32, ty.fnParamLen()));
- if (ty.fnParamLen() != 0) {
- const params = try buf.allocator.alloc(Type, ty.fnParamLen());
- defer buf.allocator.free(params);
- ty.fnParamTypes(params);
- for (params) |param_type| try writer.writeByte(genValtype(param_type));
- }
-
- // return type
- const return_type = ty.fnReturnType();
- switch (return_type.tag()) {
- .void, .noreturn => try leb.writeULEB128(writer, @as(u32, 0)),
- else => {
- try leb.writeULEB128(writer, @as(u32, 1));
- try writer.writeByte(genValtype(return_type));
- },
- }
-}
-
-pub fn genCode(buf: *ArrayList(u8), decl: *Decl) !void {
- assert(buf.items.len == 0);
- const writer = buf.writer();
-
- // Reserve space to write the size after generating the code
- try buf.resize(5);
-
- // Write the size of the locals vec
- // TODO: implement locals
- try leb.writeULEB128(writer, @as(u32, 0));
-
- // Write instructions
- // TODO: check for and handle death of instructions
- const tv = decl.typed_value.most_recent.typed_value;
- const mod_fn = tv.val.cast(Value.Payload.Function).?.func;
- for (mod_fn.analysis.success.instructions) |inst| try genInst(buf, decl, inst);
-
- // Write 'end' opcode
- try writer.writeByte(0x0B);
-
- // Fill in the size of the generated code to the reserved space at the
- // beginning of the buffer.
- const size = buf.items.len - 5 + decl.fn_link.wasm.?.idx_refs.items.len * 5;
- leb.writeUnsignedFixed(5, buf.items[0..5], @intCast(u32, size));
-}
-
-fn genInst(buf: *ArrayList(u8), decl: *Decl, inst: *Inst) !void {
- return switch (inst.tag) {
- .call => genCall(buf, decl, inst.castTag(.call).?),
- .constant => genConstant(buf, decl, inst.castTag(.constant).?),
- .dbg_stmt => {},
- .ret => genRet(buf, decl, inst.castTag(.ret).?),
- .retvoid => {},
- else => error.TODOImplementMoreWasmCodegen,
- };
-}
-
-fn genConstant(buf: *ArrayList(u8), decl: *Decl, inst: *Inst.Constant) !void {
- const writer = buf.writer();
- switch (inst.base.ty.tag()) {
- .u32 => {
- try writer.writeByte(0x41); // i32.const
- try leb.writeILEB128(writer, inst.val.toUnsignedInt());
- },
- .i32 => {
- try writer.writeByte(0x41); // i32.const
- try leb.writeILEB128(writer, inst.val.toSignedInt());
- },
- .u64 => {
- try writer.writeByte(0x42); // i64.const
- try leb.writeILEB128(writer, inst.val.toUnsignedInt());
- },
- .i64 => {
- try writer.writeByte(0x42); // i64.const
- try leb.writeILEB128(writer, inst.val.toSignedInt());
- },
- .f32 => {
- try writer.writeByte(0x43); // f32.const
- // TODO: enforce LE byte order
- try writer.writeAll(mem.asBytes(&inst.val.toFloat(f32)));
- },
- .f64 => {
- try writer.writeByte(0x44); // f64.const
- // TODO: enforce LE byte order
- try writer.writeAll(mem.asBytes(&inst.val.toFloat(f64)));
- },
- .void => {},
- else => return error.TODOImplementMoreWasmCodegen,
- }
-}
-
-fn genRet(buf: *ArrayList(u8), decl: *Decl, inst: *Inst.UnOp) !void {
- try genInst(buf, decl, inst.operand);
-}
-
-fn genCall(buf: *ArrayList(u8), decl: *Decl, inst: *Inst.Call) !void {
- const func_inst = inst.func.castTag(.constant).?;
- const func_val = func_inst.val.cast(Value.Payload.Function).?;
- const target = func_val.func.owner_decl;
- const target_ty = target.typed_value.most_recent.typed_value.ty;
-
- if (inst.args.len != 0) return error.TODOImplementMoreWasmCodegen;
-
- try buf.append(0x10); // call
-
- // The function index immediate argument will be filled in using this data
- // in link.Wasm.flush().
- try decl.fn_link.wasm.?.idx_refs.append(buf.allocator, .{
- .offset = @intCast(u32, buf.items.len),
- .decl = target,
- });
-}
diff --git a/src-self-hosted/codegen/x86.zig b/src-self-hosted/codegen/x86.zig
deleted file mode 100644
index fdad4e56db6139258e72b615a4d11dd8246b0d19..0000000000000000000000000000000000000000
--- a/src-self-hosted/codegen/x86.zig
+++ /dev/null
@@ -1,123 +0,0 @@
-const std = @import("std");
-const DW = std.dwarf;
-
-// zig fmt: off
-pub const Register = enum(u8) {
- // 0 through 7, 32-bit registers. id is int value
- eax, ecx, edx, ebx, esp, ebp, esi, edi,
-
- // 8-15, 16-bit registers. id is int value - 8.
- ax, cx, dx, bx, sp, bp, si, di,
-
- // 16-23, 8-bit registers. id is int value - 16.
- al, cl, dl, bl, ah, ch, dh, bh,
-
- /// Returns the bit-width of the register.
- pub fn size(self: @This()) u7 {
- return switch (@enumToInt(self)) {
- 0...7 => 32,
- 8...15 => 16,
- 16...23 => 8,
- else => unreachable,
- };
- }
-
- /// Returns the register's id. This is used in practically every opcode the
- /// x86 has. It is embedded in some instructions, such as the `B8 +rd` move
- /// instruction, and is used in the R/M byte.
- pub fn id(self: @This()) u3 {
- return @truncate(u3, @enumToInt(self));
- }
-
- /// Returns the index into `callee_preserved_regs`.
- pub fn allocIndex(self: Register) ?u4 {
- return switch (self) {
- .eax, .ax, .al => 0,
- .ecx, .cx, .cl => 1,
- .edx, .dx, .dl => 2,
- .esi, .si => 3,
- .edi, .di => 4,
- else => null,
- };
- }
-
- /// Convert from any register to its 32 bit alias.
- pub fn to32(self: Register) Register {
- return @intToEnum(Register, @as(u8, self.id()));
- }
-
- /// Convert from any register to its 16 bit alias.
- pub fn to16(self: Register) Register {
- return @intToEnum(Register, @as(u8, self.id()) + 8);
- }
-
- /// Convert from any register to its 8 bit alias.
- pub fn to8(self: Register) Register {
- return @intToEnum(Register, @as(u8, self.id()) + 16);
- }
-
-
- pub fn dwarfLocOp(reg: Register) u8 {
- return switch (reg.to32()) {
- .eax => DW.OP_reg0,
- .ecx => DW.OP_reg1,
- .edx => DW.OP_reg2,
- .ebx => DW.OP_reg3,
- .esp => DW.OP_reg4,
- .ebp => DW.OP_reg5,
- .esi => DW.OP_reg6,
- .edi => DW.OP_reg7,
- else => unreachable,
- };
- }
-};
-
-// zig fmt: on
-
-pub const callee_preserved_regs = [_]Register{ .eax, .ecx, .edx, .esi, .edi };
-
-// TODO add these to Register enum and corresponding dwarfLocOp
-// // Return Address register. This is stored in `0(%esp, "")` and is not a physical register.
-// RA = (8, "RA"),
-//
-// ST0 = (11, "st0"),
-// ST1 = (12, "st1"),
-// ST2 = (13, "st2"),
-// ST3 = (14, "st3"),
-// ST4 = (15, "st4"),
-// ST5 = (16, "st5"),
-// ST6 = (17, "st6"),
-// ST7 = (18, "st7"),
-//
-// XMM0 = (21, "xmm0"),
-// XMM1 = (22, "xmm1"),
-// XMM2 = (23, "xmm2"),
-// XMM3 = (24, "xmm3"),
-// XMM4 = (25, "xmm4"),
-// XMM5 = (26, "xmm5"),
-// XMM6 = (27, "xmm6"),
-// XMM7 = (28, "xmm7"),
-//
-// MM0 = (29, "mm0"),
-// MM1 = (30, "mm1"),
-// MM2 = (31, "mm2"),
-// MM3 = (32, "mm3"),
-// MM4 = (33, "mm4"),
-// MM5 = (34, "mm5"),
-// MM6 = (35, "mm6"),
-// MM7 = (36, "mm7"),
-//
-// MXCSR = (39, "mxcsr"),
-//
-// ES = (40, "es"),
-// CS = (41, "cs"),
-// SS = (42, "ss"),
-// DS = (43, "ds"),
-// FS = (44, "fs"),
-// GS = (45, "gs"),
-//
-// TR = (48, "tr"),
-// LDTR = (49, "ldtr"),
-//
-// FS_BASE = (93, "fs.base"),
-// GS_BASE = (94, "gs.base"),
diff --git a/src-self-hosted/codegen/x86_64.zig b/src-self-hosted/codegen/x86_64.zig
deleted file mode 100644
index dea39f82cdbdd2c4d6d623c6d3b697c00d9bfda1..0000000000000000000000000000000000000000
--- a/src-self-hosted/codegen/x86_64.zig
+++ /dev/null
@@ -1,220 +0,0 @@
-const std = @import("std");
-const Type = @import("../Type.zig");
-const DW = std.dwarf;
-
-// zig fmt: off
-
-/// Definitions of all of the x64 registers. The order is semantically meaningful.
-/// The registers are defined such that IDs go in descending order of 64-bit,
-/// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen
-/// registers. This results in some useful properties:
-///
-/// Any 64-bit register can be turned into its 32-bit form by adding 16, and
-/// vice versa. This also works between 32-bit and 16-bit forms. With 8-bit, it
-/// works for all except for sp, bp, si, and di, which do *not* have an 8-bit
-/// form.
-///
-/// If (register & 8) is set, the register is extended.
-///
-/// The ID can be easily determined by figuring out what range the register is
-/// in, and then subtracting the base.
-pub const Register = enum(u8) {
- // 0 through 15, 64-bit registers. 8-15 are extended.
- // id is just the int value.
- rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,
- r8, r9, r10, r11, r12, r13, r14, r15,
-
- // 16 through 31, 32-bit registers. 24-31 are extended.
- // id is int value - 16.
- eax, ecx, edx, ebx, esp, ebp, esi, edi,
- r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d,
-
- // 32-47, 16-bit registers. 40-47 are extended.
- // id is int value - 32.
- ax, cx, dx, bx, sp, bp, si, di,
- r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w,
-
- // 48-63, 8-bit registers. 56-63 are extended.
- // id is int value - 48.
- al, cl, dl, bl, ah, ch, dh, bh,
- r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b,
-
- /// Returns the bit-width of the register.
- pub fn size(self: Register) u7 {
- return switch (@enumToInt(self)) {
- 0...15 => 64,
- 16...31 => 32,
- 32...47 => 16,
- 48...64 => 8,
- else => unreachable,
- };
- }
-
- /// Returns whether the register is *extended*. Extended registers are the
- /// new registers added with amd64, r8 through r15. This also includes any
- /// other variant of access to those registers, such as r8b, r15d, and so
- /// on. This is needed because access to these registers requires special
- /// handling via the REX prefix, via the B or R bits, depending on context.
- pub fn isExtended(self: Register) bool {
- return @enumToInt(self) & 0x08 != 0;
- }
-
- /// This returns the 4-bit register ID, which is used in practically every
- /// opcode. Note that bit 3 (the highest bit) is *never* used directly in
- /// an instruction (@see isExtended), and requires special handling. The
- /// lower three bits are often embedded directly in instructions (such as
- /// the B8 variant of moves), or used in R/M bytes.
- pub fn id(self: Register) u4 {
- return @truncate(u4, @enumToInt(self));
- }
-
- /// Returns the index into `callee_preserved_regs`.
- pub fn allocIndex(self: Register) ?u4 {
- return switch (self) {
- .rax, .eax, .ax, .al => 0,
- .rcx, .ecx, .cx, .cl => 1,
- .rdx, .edx, .dx, .dl => 2,
- .rsi, .esi, .si => 3,
- .rdi, .edi, .di => 4,
- .r8, .r8d, .r8w, .r8b => 5,
- .r9, .r9d, .r9w, .r9b => 6,
- .r10, .r10d, .r10w, .r10b => 7,
- .r11, .r11d, .r11w, .r11b => 8,
- else => null,
- };
- }
-
- /// Convert from any register to its 64 bit alias.
- pub fn to64(self: Register) Register {
- return @intToEnum(Register, self.id());
- }
-
- /// Convert from any register to its 32 bit alias.
- pub fn to32(self: Register) Register {
- return @intToEnum(Register, @as(u8, self.id()) + 16);
- }
-
- /// Convert from any register to its 16 bit alias.
- pub fn to16(self: Register) Register {
- return @intToEnum(Register, @as(u8, self.id()) + 32);
- }
-
- /// Convert from any register to its 8 bit alias.
- pub fn to8(self: Register) Register {
- return @intToEnum(Register, @as(u8, self.id()) + 48);
- }
-
- pub fn dwarfLocOp(self: Register) u8 {
- return switch (self.to64()) {
- .rax => DW.OP_reg0,
- .rdx => DW.OP_reg1,
- .rcx => DW.OP_reg2,
- .rbx => DW.OP_reg3,
- .rsi => DW.OP_reg4,
- .rdi => DW.OP_reg5,
- .rbp => DW.OP_reg6,
- .rsp => DW.OP_reg7,
-
- .r8 => DW.OP_reg8,
- .r9 => DW.OP_reg9,
- .r10 => DW.OP_reg10,
- .r11 => DW.OP_reg11,
- .r12 => DW.OP_reg12,
- .r13 => DW.OP_reg13,
- .r14 => DW.OP_reg14,
- .r15 => DW.OP_reg15,
-
- else => unreachable,
- };
- }
-};
-
-// zig fmt: on
-
-/// These registers belong to the called function.
-pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
-pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
-pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
-
-// TODO add these registers to the enum and populate dwarfLocOp
-// // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register.
-// RA = (16, "RA"),
-//
-// XMM0 = (17, "xmm0"),
-// XMM1 = (18, "xmm1"),
-// XMM2 = (19, "xmm2"),
-// XMM3 = (20, "xmm3"),
-// XMM4 = (21, "xmm4"),
-// XMM5 = (22, "xmm5"),
-// XMM6 = (23, "xmm6"),
-// XMM7 = (24, "xmm7"),
-//
-// XMM8 = (25, "xmm8"),
-// XMM9 = (26, "xmm9"),
-// XMM10 = (27, "xmm10"),
-// XMM11 = (28, "xmm11"),
-// XMM12 = (29, "xmm12"),
-// XMM13 = (30, "xmm13"),
-// XMM14 = (31, "xmm14"),
-// XMM15 = (32, "xmm15"),
-//
-// ST0 = (33, "st0"),
-// ST1 = (34, "st1"),
-// ST2 = (35, "st2"),
-// ST3 = (36, "st3"),
-// ST4 = (37, "st4"),
-// ST5 = (38, "st5"),
-// ST6 = (39, "st6"),
-// ST7 = (40, "st7"),
-//
-// MM0 = (41, "mm0"),
-// MM1 = (42, "mm1"),
-// MM2 = (43, "mm2"),
-// MM3 = (44, "mm3"),
-// MM4 = (45, "mm4"),
-// MM5 = (46, "mm5"),
-// MM6 = (47, "mm6"),
-// MM7 = (48, "mm7"),
-//
-// RFLAGS = (49, "rFLAGS"),
-// ES = (50, "es"),
-// CS = (51, "cs"),
-// SS = (52, "ss"),
-// DS = (53, "ds"),
-// FS = (54, "fs"),
-// GS = (55, "gs"),
-//
-// FS_BASE = (58, "fs.base"),
-// GS_BASE = (59, "gs.base"),
-//
-// TR = (62, "tr"),
-// LDTR = (63, "ldtr"),
-// MXCSR = (64, "mxcsr"),
-// FCW = (65, "fcw"),
-// FSW = (66, "fsw"),
-//
-// XMM16 = (67, "xmm16"),
-// XMM17 = (68, "xmm17"),
-// XMM18 = (69, "xmm18"),
-// XMM19 = (70, "xmm19"),
-// XMM20 = (71, "xmm20"),
-// XMM21 = (72, "xmm21"),
-// XMM22 = (73, "xmm22"),
-// XMM23 = (74, "xmm23"),
-// XMM24 = (75, "xmm24"),
-// XMM25 = (76, "xmm25"),
-// XMM26 = (77, "xmm26"),
-// XMM27 = (78, "xmm27"),
-// XMM28 = (79, "xmm28"),
-// XMM29 = (80, "xmm29"),
-// XMM30 = (81, "xmm30"),
-// XMM31 = (82, "xmm31"),
-//
-// K0 = (118, "k0"),
-// K1 = (119, "k1"),
-// K2 = (120, "k2"),
-// K3 = (121, "k3"),
-// K4 = (122, "k4"),
-// K5 = (123, "k5"),
-// K6 = (124, "k6"),
-// K7 = (125, "k7"),
diff --git a/src-self-hosted/dep_tokenizer.zig b/src-self-hosted/dep_tokenizer.zig
deleted file mode 100644
index 20324cbf0c1f5c764384f802225fedca2a3f5a06..0000000000000000000000000000000000000000
--- a/src-self-hosted/dep_tokenizer.zig
+++ /dev/null
@@ -1,1039 +0,0 @@
-const std = @import("std");
-const testing = std.testing;
-
-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;
- }
-
- // eof, handle maybe incomplete token
- if (self.index == 0) return null;
- const idx = self.index - 1;
- switch (self.state) {
- .lhs,
- .rhs,
- .rhs_continuation,
- .rhs_continuation_linefeed,
- => {},
- .target => |target| {
- return self.errorPosition(idx, target.span(), "incomplete target", .{});
- },
- .target_reverse_solidus,
- .target_dollar_sign,
- => {
- const index = self.index - 1;
- return self.errorIllegalChar(idx, self.bytes[idx], "incomplete escape", .{});
- },
- .target_colon => |target| {
- const bytes = target.span();
- if (bytes.len != 0) {
- self.index += 1;
- self.state = State{ .rhs = {} };
- return Token{ .id = .target, .bytes = bytes };
- }
- // silently ignore null target
- self.state = State{ .lhs = {} };
- },
- .target_colon_reverse_solidus => |target| {
- const bytes = target.span();
- if (bytes.len != 0) {
- self.index += 1;
- self.state = State{ .rhs = {} };
- return Token{ .id = .target, .bytes = bytes };
- }
- // silently ignore null target
- self.state = State{ .lhs = {} };
- },
- .prereq_quote => |prereq| {
- return self.errorPosition(idx, prereq.span(), "incomplete quoted prerequisite", .{});
- },
- .prereq => |prereq| {
- const bytes = prereq.span();
- self.state = State{ .lhs = {} };
- return Token{ .id = .prereq, .bytes = bytes };
- },
- .prereq_continuation => |prereq| {
- const bytes = prereq.span();
- self.state = State{ .lhs = {} };
- return Token{ .id = .prereq, .bytes = bytes };
- },
- .prereq_continuation_linefeed => |prereq| {
- const bytes = prereq.span();
- self.state = State{ .lhs = {} };
- return Token{ .id = .prereq, .bytes = bytes };
- },
- }
- return null;
- }
-
- 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(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(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;
- }
-
- const Error = error{
- OutOfMemory,
- InvalidInput,
- };
-
- 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,
- bytes: []const u8,
-
- const ID = enum {
- target,
- prereq,
- };
- };
-};
-
-test "empty file" {
- try depTokenizer("", "");
-}
-
-test "empty whitespace" {
- try depTokenizer("\n", "");
- try depTokenizer("\r", "");
- try depTokenizer("\r\n", "");
- try depTokenizer(" ", "");
-}
-
-test "empty colon" {
- try depTokenizer(":", "");
- try depTokenizer("\n:", "");
- try depTokenizer("\r:", "");
- try depTokenizer("\r\n:", "");
- try depTokenizer(" :", "");
-}
-
-test "empty target" {
- try depTokenizer("foo.o:", "target = {foo.o}");
- try depTokenizer(
- \\foo.o:
- \\bar.o:
- \\abcd.o:
- ,
- \\target = {foo.o}
- \\target = {bar.o}
- \\target = {abcd.o}
- );
-}
-
-test "whitespace empty target" {
- try depTokenizer("\nfoo.o:", "target = {foo.o}");
- try depTokenizer("\rfoo.o:", "target = {foo.o}");
- try depTokenizer("\r\nfoo.o:", "target = {foo.o}");
- try depTokenizer(" foo.o:", "target = {foo.o}");
-}
-
-test "escape empty target" {
- try depTokenizer("\\ foo.o:", "target = { foo.o}");
- try depTokenizer("\\#foo.o:", "target = {#foo.o}");
- try depTokenizer("\\\\foo.o:", "target = {\\foo.o}");
- try depTokenizer("$$foo.o:", "target = {$foo.o}");
-}
-
-test "empty target linefeeds" {
- try depTokenizer("\n", "");
- try depTokenizer("\r\n", "");
-
- const expect = "target = {foo.o}";
- try depTokenizer(
- \\foo.o:
- , expect);
- try depTokenizer(
- \\foo.o:
- \\
- , expect);
- try depTokenizer(
- \\foo.o:
- , expect);
- try depTokenizer(
- \\foo.o:
- \\
- , expect);
-}
-
-test "empty target linefeeds + continuations" {
- const expect = "target = {foo.o}";
- try depTokenizer(
- \\foo.o:\
- , expect);
- try depTokenizer(
- \\foo.o:\
- \\
- , expect);
- try depTokenizer(
- \\foo.o:\
- , expect);
- try depTokenizer(
- \\foo.o:\
- \\
- , expect);
-}
-
-test "empty target linefeeds + hspace + continuations" {
- const expect = "target = {foo.o}";
- try depTokenizer(
- \\foo.o: \
- , expect);
- try depTokenizer(
- \\foo.o: \
- \\
- , expect);
- try depTokenizer(
- \\foo.o: \
- , expect);
- try depTokenizer(
- \\foo.o: \
- \\
- , expect);
-}
-
-test "prereq" {
- const expect =
- \\target = {foo.o}
- \\prereq = {foo.c}
- ;
- try depTokenizer("foo.o: foo.c", expect);
- try depTokenizer(
- \\foo.o: \
- \\foo.c
- , expect);
- try depTokenizer(
- \\foo.o: \
- \\ foo.c
- , expect);
- try depTokenizer(
- \\foo.o: \
- \\ foo.c
- , expect);
-}
-
-test "prereq continuation" {
- const expect =
- \\target = {foo.o}
- \\prereq = {foo.h}
- \\prereq = {bar.h}
- ;
- try depTokenizer(
- \\foo.o: foo.h\
- \\bar.h
- , expect);
- try depTokenizer(
- \\foo.o: foo.h\
- \\bar.h
- , expect);
-}
-
-test "multiple prereqs" {
- const expect =
- \\target = {foo.o}
- \\prereq = {foo.c}
- \\prereq = {foo.h}
- \\prereq = {bar.h}
- ;
- try depTokenizer("foo.o: foo.c foo.h bar.h", expect);
- try depTokenizer(
- \\foo.o: \
- \\foo.c foo.h bar.h
- , expect);
- try depTokenizer(
- \\foo.o: foo.c foo.h bar.h\
- , expect);
- try depTokenizer(
- \\foo.o: foo.c foo.h bar.h\
- \\
- , expect);
- try depTokenizer(
- \\foo.o: \
- \\foo.c \
- \\ foo.h\
- \\bar.h
- \\
- , expect);
- try depTokenizer(
- \\foo.o: \
- \\foo.c \
- \\ foo.h\
- \\bar.h\
- \\
- , expect);
- try depTokenizer(
- \\foo.o: \
- \\foo.c \
- \\ foo.h\
- \\bar.h\
- , expect);
-}
-
-test "multiple targets and prereqs" {
- try depTokenizer(
- \\foo.o: foo.c
- \\bar.o: bar.c a.h b.h c.h
- \\abc.o: abc.c \
- \\ one.h two.h \
- \\ three.h four.h
- ,
- \\target = {foo.o}
- \\prereq = {foo.c}
- \\target = {bar.o}
- \\prereq = {bar.c}
- \\prereq = {a.h}
- \\prereq = {b.h}
- \\prereq = {c.h}
- \\target = {abc.o}
- \\prereq = {abc.c}
- \\prereq = {one.h}
- \\prereq = {two.h}
- \\prereq = {three.h}
- \\prereq = {four.h}
- );
- try depTokenizer(
- \\ascii.o: ascii.c
- \\base64.o: base64.c stdio.h
- \\elf.o: elf.c a.h b.h c.h
- \\macho.o: \
- \\ macho.c\
- \\ a.h b.h c.h
- ,
- \\target = {ascii.o}
- \\prereq = {ascii.c}
- \\target = {base64.o}
- \\prereq = {base64.c}
- \\prereq = {stdio.h}
- \\target = {elf.o}
- \\prereq = {elf.c}
- \\prereq = {a.h}
- \\prereq = {b.h}
- \\prereq = {c.h}
- \\target = {macho.o}
- \\prereq = {macho.c}
- \\prereq = {a.h}
- \\prereq = {b.h}
- \\prereq = {c.h}
- );
- try depTokenizer(
- \\a$$scii.o: ascii.c
- \\\\base64.o: "\base64.c" "s t#dio.h"
- \\e\\lf.o: "e\lf.c" "a.h$$" "$$b.h c.h$$"
- \\macho.o: \
- \\ "macho!.c" \
- \\ a.h b.h c.h
- ,
- \\target = {a$scii.o}
- \\prereq = {ascii.c}
- \\target = {\base64.o}
- \\prereq = {\base64.c}
- \\prereq = {s t#dio.h}
- \\target = {e\lf.o}
- \\prereq = {e\lf.c}
- \\prereq = {a.h$$}
- \\prereq = {$$b.h c.h$$}
- \\target = {macho.o}
- \\prereq = {macho!.c}
- \\prereq = {a.h}
- \\prereq = {b.h}
- \\prereq = {c.h}
- );
-}
-
-test "windows quoted prereqs" {
- try depTokenizer(
- \\c:\foo.o: "C:\Program Files (x86)\Microsoft Visual Studio\foo.c"
- \\c:\foo2.o: "C:\Program Files (x86)\Microsoft Visual Studio\foo2.c" \
- \\ "C:\Program Files (x86)\Microsoft Visual Studio\foo1.h" \
- \\ "C:\Program Files (x86)\Microsoft Visual Studio\foo2.h"
- ,
- \\target = {c:\foo.o}
- \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo.c}
- \\target = {c:\foo2.o}
- \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo2.c}
- \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo1.h}
- \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo2.h}
- );
-}
-
-test "windows mixed prereqs" {
- try depTokenizer(
- \\cimport.o: \
- \\ C:\msys64\home\anon\project\zig\master\zig-cache\o\qhvhbUo7GU5iKyQ5mpA8TcQpncCYaQu0wwvr3ybiSTj_Dtqi1Nmcb70kfODJ2Qlg\cimport.h \
- \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\stdio.h" \
- \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt.h" \
- \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime.h" \
- \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\sal.h" \
- \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\concurrencysal.h" \
- \\ C:\msys64\opt\zig\lib\zig\include\vadefs.h \
- \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vadefs.h" \
- \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstdio.h" \
- \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_stdio_config.h" \
- \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\string.h" \
- \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memory.h" \
- \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memcpy_s.h" \
- \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\errno.h" \
- \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime_string.h" \
- \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstring.h"
- ,
- \\target = {cimport.o}
- \\prereq = {C:\msys64\home\anon\project\zig\master\zig-cache\o\qhvhbUo7GU5iKyQ5mpA8TcQpncCYaQu0wwvr3ybiSTj_Dtqi1Nmcb70kfODJ2Qlg\cimport.h}
- \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\stdio.h}
- \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt.h}
- \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime.h}
- \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\sal.h}
- \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\concurrencysal.h}
- \\prereq = {C:\msys64\opt\zig\lib\zig\include\vadefs.h}
- \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vadefs.h}
- \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstdio.h}
- \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_stdio_config.h}
- \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\string.h}
- \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memory.h}
- \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memcpy_s.h}
- \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\errno.h}
- \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime_string.h}
- \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstring.h}
- );
-}
-
-test "funky targets" {
- try depTokenizer(
- \\C:\Users\anon\foo.o:
- \\C:\Users\anon\foo\ .o:
- \\C:\Users\anon\foo\#.o:
- \\C:\Users\anon\foo$$.o:
- \\C:\Users\anon\\\ foo.o:
- \\C:\Users\anon\\#foo.o:
- \\C:\Users\anon\$$foo.o:
- \\C:\Users\anon\\\ \ \ \ \ foo.o:
- ,
- \\target = {C:\Users\anon\foo.o}
- \\target = {C:\Users\anon\foo .o}
- \\target = {C:\Users\anon\foo#.o}
- \\target = {C:\Users\anon\foo$.o}
- \\target = {C:\Users\anon\ foo.o}
- \\target = {C:\Users\anon\#foo.o}
- \\target = {C:\Users\anon\$foo.o}
- \\target = {C:\Users\anon\ foo.o}
- );
-}
-
-test "error incomplete escape - reverse_solidus" {
- try depTokenizer("\\",
- \\ERROR: illegal char '\' at position 0: incomplete escape
- );
- try depTokenizer("\t\\",
- \\ERROR: illegal char '\' at position 1: incomplete escape
- );
- try depTokenizer("\n\\",
- \\ERROR: illegal char '\' at position 1: incomplete escape
- );
- try depTokenizer("\r\\",
- \\ERROR: illegal char '\' at position 1: incomplete escape
- );
- try depTokenizer("\r\n\\",
- \\ERROR: illegal char '\' at position 2: incomplete escape
- );
- try depTokenizer(" \\",
- \\ERROR: illegal char '\' at position 1: incomplete escape
- );
-}
-
-test "error incomplete escape - dollar_sign" {
- try depTokenizer("$",
- \\ERROR: illegal char '$' at position 0: incomplete escape
- );
- try depTokenizer("\t$",
- \\ERROR: illegal char '$' at position 1: incomplete escape
- );
- try depTokenizer("\n$",
- \\ERROR: illegal char '$' at position 1: incomplete escape
- );
- try depTokenizer("\r$",
- \\ERROR: illegal char '$' at position 1: incomplete escape
- );
- try depTokenizer("\r\n$",
- \\ERROR: illegal char '$' at position 2: incomplete escape
- );
- try depTokenizer(" $",
- \\ERROR: illegal char '$' at position 1: incomplete escape
- );
-}
-
-test "error incomplete target" {
- try depTokenizer("foo.o",
- \\ERROR: incomplete target 'foo.o' at position 0
- );
- try depTokenizer("\tfoo.o",
- \\ERROR: incomplete target 'foo.o' at position 1
- );
- try depTokenizer("\nfoo.o",
- \\ERROR: incomplete target 'foo.o' at position 1
- );
- try depTokenizer("\rfoo.o",
- \\ERROR: incomplete target 'foo.o' at position 1
- );
- try depTokenizer("\r\nfoo.o",
- \\ERROR: incomplete target 'foo.o' at position 2
- );
- try depTokenizer(" foo.o",
- \\ERROR: incomplete target 'foo.o' at position 1
- );
-
- try depTokenizer("\\ foo.o",
- \\ERROR: incomplete target ' foo.o' at position 1
- );
- try depTokenizer("\\#foo.o",
- \\ERROR: incomplete target '#foo.o' at position 1
- );
- try depTokenizer("\\\\foo.o",
- \\ERROR: incomplete target '\foo.o' at position 1
- );
- try depTokenizer("$$foo.o",
- \\ERROR: incomplete target '$foo.o' at position 1
- );
-}
-
-test "error illegal char at position - bad target escape" {
- try depTokenizer("\\\t",
- \\ERROR: illegal char \x09 at position 1: bad target escape
- );
- try depTokenizer("\\\n",
- \\ERROR: illegal char \x0A at position 1: bad target escape
- );
- try depTokenizer("\\\r",
- \\ERROR: illegal char \x0D at position 1: bad target escape
- );
- try depTokenizer("\\\r\n",
- \\ERROR: illegal char \x0D at position 1: bad target escape
- );
-}
-
-test "error illegal char at position - execting dollar_sign" {
- try depTokenizer("$\t",
- \\ERROR: illegal char \x09 at position 1: expecting '$'
- );
- try depTokenizer("$\n",
- \\ERROR: illegal char \x0A at position 1: expecting '$'
- );
- try depTokenizer("$\r",
- \\ERROR: illegal char \x0D at position 1: expecting '$'
- );
- try depTokenizer("$\r\n",
- \\ERROR: illegal char \x0D at position 1: expecting '$'
- );
-}
-
-test "error illegal char at position - invalid target" {
- try depTokenizer("foo\t.o",
- \\ERROR: illegal char \x09 at position 3: invalid target
- );
- try depTokenizer("foo\n.o",
- \\ERROR: illegal char \x0A at position 3: invalid target
- );
- try depTokenizer("foo\r.o",
- \\ERROR: illegal char \x0D at position 3: invalid target
- );
- try depTokenizer("foo\r\n.o",
- \\ERROR: illegal char \x0D at position 3: invalid target
- );
-}
-
-test "error target - continuation expecting end-of-line" {
- try depTokenizer("foo.o: \\\t",
- \\target = {foo.o}
- \\ERROR: illegal char \x09 at position 8: continuation expecting end-of-line
- );
- try depTokenizer("foo.o: \\ ",
- \\target = {foo.o}
- \\ERROR: illegal char \x20 at position 8: continuation expecting end-of-line
- );
- try depTokenizer("foo.o: \\x",
- \\target = {foo.o}
- \\ERROR: illegal char 'x' at position 8: continuation expecting end-of-line
- );
- try depTokenizer("foo.o: \\\x0dx",
- \\target = {foo.o}
- \\ERROR: illegal char 'x' at position 9: continuation expecting end-of-line
- );
-}
-
-test "error prereq - continuation expecting end-of-line" {
- try depTokenizer("foo.o: foo.h\\\x0dx",
- \\target = {foo.o}
- \\ERROR: illegal char 'x' at position 14: 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);
- const arena = &arena_allocator.allocator;
- defer arena_allocator.deinit();
-
- var it = Tokenizer.init(arena, input);
- var buffer = try std.ArrayListSentineled(u8, 0).initSize(arena, 0);
- 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;
- 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]);
- }
- try buffer.appendSlice("}");
- i += 1;
- }
- const got: []const u8 = buffer.span();
-
- if (std.mem.eql(u8, expect, got)) {
- testing.expect(true);
- return;
- }
-
- var out = makeOutput(std.fs.File.write, try std.io.getStdErr());
-
- try out.write("\n");
- try printSection(&out, "<<<< input", input);
- try printSection(&out, "==== expect", expect);
- try printSection(&out, ">>>> got", got);
- try printRuler(&out);
-
- testing.expect(false);
-}
-
-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");
-}
-
-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);
- var i: usize = text.len;
- const end = 79;
- while (i < 79) : (i += 1) {
- try out.write([_]u8{label[0]});
- }
- try out.write("\n");
-}
-
-fn printRuler(out: anytype) !void {
- var i: usize = 0;
- const end = 79;
- while (i < 79) : (i += 1) {
- try out.write("-");
- }
- try out.write("\n");
-}
-
-fn hexDump(out: anytype, bytes: []const u8) !void {
- const n16 = bytes.len >> 4;
- var line: usize = 0;
- var offset: usize = 0;
- while (line < n16) : (line += 1) {
- try hexDump16(out, offset, bytes[offset .. offset + 16]);
- offset += 16;
- }
-
- const n = bytes.len & 0x0f;
- if (n > 0) {
- try printDecValue(out, offset, 8);
- try out.write(":");
- try out.write(" ");
- var end1 = std.math.min(offset + n, offset + 8);
- for (bytes[offset..end1]) |b| {
- try out.write(" ");
- try printHexValue(out, b, 2);
- }
- var end2 = offset + n;
- if (end2 > end1) {
- try out.write(" ");
- for (bytes[end1..end2]) |b| {
- try out.write(" ");
- try printHexValue(out, b, 2);
- }
- }
- const short = 16 - n;
- var i: usize = 0;
- while (i < short) : (i += 1) {
- try out.write(" ");
- }
- if (end2 > end1) {
- try out.write(" |");
- } else {
- try out.write(" |");
- }
- try printCharValues(out, bytes[offset..end2]);
- try out.write("|\n");
- offset += n;
- }
-
- try printDecValue(out, offset, 8);
- try out.write(":");
- try out.write("\n");
-}
-
-fn hexDump16(out: anytype, offset: usize, bytes: []const u8) !void {
- try printDecValue(out, offset, 8);
- try out.write(":");
- try out.write(" ");
- for (bytes[0..8]) |b| {
- try out.write(" ");
- try printHexValue(out, b, 2);
- }
- try out.write(" ");
- for (bytes[8..16]) |b| {
- try out.write(" ");
- try printHexValue(out, b, 2);
- }
- try out.write(" |");
- try printCharValues(out, bytes);
- try out.write("|\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]);
-}
-
-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]);
-}
-
-fn printCharValues(out: anytype, bytes: []const u8) !void {
- for (bytes) |b| {
- try out.write(&[_]u8{printable_char_tab[b]});
- }
-}
-
-fn printUnderstandableChar(buffer: *std.ArrayListSentineled(u8, 0), char: u8) !void {
- if (!std.ascii.isPrint(char) or char == ' ') {
- try buffer.outStream().print("\\x{X:2}", .{char});
- } else {
- try buffer.appendSlice("'");
- try buffer.append(printable_char_tab[char]);
- try buffer.appendSlice("'");
- }
-}
-
-// zig fmt: off
-const printable_char_tab: []const 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/introspect.zig b/src-self-hosted/introspect.zig
deleted file mode 100644
index 80f10c8656780fe05edd120321e5e7f26d3454de..0000000000000000000000000000000000000000
--- 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/ir.zig b/src-self-hosted/ir.zig
deleted file mode 100644
index 26afa52929e38592d1fd381cea25acc16115c315..0000000000000000000000000000000000000000
--- a/src-self-hosted/ir.zig
+++ /dev/null
@@ -1,465 +0,0 @@
-const std = @import("std");
-const Value = @import("value.zig").Value;
-const Type = @import("type.zig").Type;
-const Module = @import("Module.zig");
-const assert = std.debug.assert;
-const codegen = @import("codegen.zig");
-const ast = std.zig.ast;
-
-/// These are in-memory, analyzed instructions. See `zir.Inst` for the representation
-/// of instructions that correspond to the ZIR text format.
-/// This struct owns the `Value` and `Type` memory. When the struct is deallocated,
-/// so are the `Value` and `Type`. The value of a constant must be copied into
-/// a memory location for the value to survive after a const instruction.
-pub const Inst = struct {
- tag: Tag,
- /// Each bit represents the index of an `Inst` parameter in the `args` field.
- /// If a bit is set, it marks the end of the lifetime of the corresponding
- /// instruction parameter. For example, 0b101 means that the first and
- /// third `Inst` parameters' lifetimes end after this instruction, and will
- /// not have any more following references.
- /// The most significant bit being set means that the instruction itself is
- /// never referenced, in other words its lifetime ends as soon as it finishes.
- /// If bit 15 (0b1xxx_xxxx_xxxx_xxxx) is set, it means this instruction itself is unreferenced.
- /// If bit 14 (0bx1xx_xxxx_xxxx_xxxx) is set, it means this is a special case and the
- /// lifetimes of operands are encoded elsewhere.
- deaths: DeathsInt = undefined,
- ty: Type,
- /// Byte offset into the source.
- src: usize,
-
- pub const DeathsInt = u16;
- pub const DeathsBitIndex = std.math.Log2Int(DeathsInt);
- pub const unreferenced_bit_index = @typeInfo(DeathsInt).Int.bits - 1;
- pub const deaths_bits = unreferenced_bit_index - 1;
-
- pub fn isUnused(self: Inst) bool {
- return (self.deaths & (1 << unreferenced_bit_index)) != 0;
- }
-
- pub fn operandDies(self: Inst, index: DeathsBitIndex) bool {
- assert(index < deaths_bits);
- return @truncate(u1, self.deaths >> index) != 0;
- }
-
- pub fn clearOperandDeath(self: *Inst, index: DeathsBitIndex) void {
- assert(index < deaths_bits);
- self.deaths &= ~(@as(DeathsInt, 1) << index);
- }
-
- pub fn specialOperandDeaths(self: Inst) bool {
- return (self.deaths & (1 << deaths_bits)) != 0;
- }
-
- pub const Tag = enum {
- add,
- alloc,
- arg,
- assembly,
- bitcast,
- block,
- br,
- breakpoint,
- brvoid,
- call,
- cmp_lt,
- cmp_lte,
- cmp_eq,
- cmp_gte,
- cmp_gt,
- cmp_neq,
- condbr,
- constant,
- dbg_stmt,
- isnonnull,
- isnull,
- iserr,
- /// Read a value from a pointer.
- load,
- loop,
- ptrtoint,
- ref,
- ret,
- retvoid,
- varptr,
- /// Write a value to a pointer. LHS is pointer, RHS is value.
- store,
- sub,
- unreach,
- not,
- floatcast,
- intcast,
- unwrap_optional,
- wrap_optional,
-
- pub fn Type(tag: Tag) type {
- return switch (tag) {
- .alloc,
- .retvoid,
- .unreach,
- .breakpoint,
- .dbg_stmt,
- => NoOp,
-
- .ref,
- .ret,
- .bitcast,
- .not,
- .isnonnull,
- .isnull,
- .iserr,
- .ptrtoint,
- .floatcast,
- .intcast,
- .load,
- .unwrap_optional,
- .wrap_optional,
- => UnOp,
-
- .add,
- .sub,
- .cmp_lt,
- .cmp_lte,
- .cmp_eq,
- .cmp_gte,
- .cmp_gt,
- .cmp_neq,
- .store,
- => BinOp,
-
- .arg => Arg,
- .assembly => Assembly,
- .block => Block,
- .br => Br,
- .brvoid => BrVoid,
- .call => Call,
- .condbr => CondBr,
- .constant => Constant,
- .loop => Loop,
- .varptr => VarPtr,
- };
- }
-
- pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
- return switch (op) {
- .lt => .cmp_lt,
- .lte => .cmp_lte,
- .eq => .cmp_eq,
- .gte => .cmp_gte,
- .gt => .cmp_gt,
- .neq => .cmp_neq,
- };
- }
- };
-
- /// Prefer `castTag` to this.
- pub fn cast(base: *Inst, comptime T: type) ?*T {
- if (@hasField(T, "base_tag")) {
- return base.castTag(T.base_tag);
- }
- inline for (@typeInfo(Tag).Enum.fields) |field| {
- const tag = @intToEnum(Tag, field.value);
- if (base.tag == tag) {
- if (T == tag.Type()) {
- return @fieldParentPtr(T, "base", base);
- }
- return null;
- }
- }
- unreachable;
- }
-
- pub fn castTag(base: *Inst, comptime tag: Tag) ?*tag.Type() {
- if (base.tag == tag) {
- return @fieldParentPtr(tag.Type(), "base", base);
- }
- return null;
- }
-
- pub fn Args(comptime T: type) type {
- return std.meta.fieldInfo(T, "args").field_type;
- }
-
- /// Returns `null` if runtime-known.
- pub fn value(base: *Inst) ?Value {
- if (base.ty.onePossibleValue()) |opv| return opv;
-
- const inst = base.cast(Constant) orelse return null;
- return inst.val;
- }
-
- pub fn cmpOperator(base: *Inst) ?std.math.CompareOperator {
- return switch (base.tag) {
- .cmp_lt => .lt,
- .cmp_lte => .lte,
- .cmp_eq => .eq,
- .cmp_gte => .gte,
- .cmp_gt => .gt,
- .cmp_neq => .neq,
- else => null,
- };
- }
-
- pub fn operandCount(base: *Inst) usize {
- inline for (@typeInfo(Tag).Enum.fields) |field| {
- const tag = @intToEnum(Tag, field.value);
- if (tag == base.tag) {
- return @fieldParentPtr(tag.Type(), "base", base).operandCount();
- }
- }
- unreachable;
- }
-
- pub fn getOperand(base: *Inst, index: usize) ?*Inst {
- inline for (@typeInfo(Tag).Enum.fields) |field| {
- const tag = @intToEnum(Tag, field.value);
- if (tag == base.tag) {
- return @fieldParentPtr(tag.Type(), "base", base).getOperand(index);
- }
- }
- unreachable;
- }
-
- pub fn breakBlock(base: *Inst) ?*Block {
- return switch (base.tag) {
- .br => base.castTag(.br).?.block,
- .brvoid => base.castTag(.brvoid).?.block,
- else => null,
- };
- }
-
- pub const NoOp = struct {
- base: Inst,
-
- pub fn operandCount(self: *const NoOp) usize {
- return 0;
- }
- pub fn getOperand(self: *const NoOp, index: usize) ?*Inst {
- return null;
- }
- };
-
- pub const UnOp = struct {
- base: Inst,
- operand: *Inst,
-
- pub fn operandCount(self: *const UnOp) usize {
- return 1;
- }
- pub fn getOperand(self: *const UnOp, index: usize) ?*Inst {
- if (index == 0)
- return self.operand;
- return null;
- }
- };
-
- pub const BinOp = struct {
- base: Inst,
- lhs: *Inst,
- rhs: *Inst,
-
- pub fn operandCount(self: *const BinOp) usize {
- return 2;
- }
- pub fn getOperand(self: *const BinOp, index: usize) ?*Inst {
- var i = index;
-
- if (i < 1)
- return self.lhs;
- i -= 1;
-
- if (i < 1)
- return self.rhs;
- i -= 1;
-
- return null;
- }
- };
-
- pub const Arg = struct {
- pub const base_tag = Tag.arg;
-
- base: Inst,
- name: [*:0]const u8,
-
- pub fn operandCount(self: *const Arg) usize {
- return 0;
- }
- pub fn getOperand(self: *const Arg, index: usize) ?*Inst {
- return null;
- }
- };
-
- pub const Assembly = struct {
- pub const base_tag = Tag.assembly;
-
- base: Inst,
- asm_source: []const u8,
- is_volatile: bool,
- output: ?[]const u8,
- inputs: []const []const u8,
- clobbers: []const []const u8,
- args: []const *Inst,
-
- pub fn operandCount(self: *const Assembly) usize {
- return self.args.len;
- }
- pub fn getOperand(self: *const Assembly, index: usize) ?*Inst {
- if (index < self.args.len)
- return self.args[index];
- return null;
- }
- };
-
- pub const Block = struct {
- pub const base_tag = Tag.block;
-
- base: Inst,
- body: Body,
- /// This memory is reserved for codegen code to do whatever it needs to here.
- codegen: codegen.BlockData = .{},
-
- pub fn operandCount(self: *const Block) usize {
- return 0;
- }
- pub fn getOperand(self: *const Block, index: usize) ?*Inst {
- return null;
- }
- };
-
- pub const Br = struct {
- pub const base_tag = Tag.br;
-
- base: Inst,
- block: *Block,
- operand: *Inst,
-
- pub fn operandCount(self: *const Br) usize {
- return 0;
- }
- pub fn getOperand(self: *const Br, index: usize) ?*Inst {
- if (index == 0)
- return self.operand;
- return null;
- }
- };
-
- pub const BrVoid = struct {
- pub const base_tag = Tag.brvoid;
-
- base: Inst,
- block: *Block,
-
- pub fn operandCount(self: *const BrVoid) usize {
- return 0;
- }
- pub fn getOperand(self: *const BrVoid, index: usize) ?*Inst {
- return null;
- }
- };
-
- pub const Call = struct {
- pub const base_tag = Tag.call;
-
- base: Inst,
- func: *Inst,
- args: []const *Inst,
-
- pub fn operandCount(self: *const Call) usize {
- return self.args.len + 1;
- }
- pub fn getOperand(self: *const Call, index: usize) ?*Inst {
- var i = index;
-
- if (i < 1)
- return self.func;
- i -= 1;
-
- if (i < self.args.len)
- return self.args[i];
- i -= self.args.len;
-
- return null;
- }
- };
-
- pub const CondBr = struct {
- pub const base_tag = Tag.condbr;
-
- base: Inst,
- condition: *Inst,
- then_body: Body,
- else_body: Body,
- /// Set of instructions whose lifetimes end at the start of one of the branches.
- /// The `then` branch is first: `deaths[0..then_death_count]`.
- /// The `else` branch is next: `(deaths + then_death_count)[0..else_death_count]`.
- deaths: [*]*Inst = undefined,
- then_death_count: u32 = 0,
- else_death_count: u32 = 0,
-
- pub fn operandCount(self: *const CondBr) usize {
- return 1;
- }
- pub fn getOperand(self: *const CondBr, index: usize) ?*Inst {
- var i = index;
-
- if (i < 1)
- return self.condition;
- i -= 1;
-
- return null;
- }
- pub fn thenDeaths(self: *const CondBr) []*Inst {
- return self.deaths[0..self.then_death_count];
- }
- pub fn elseDeaths(self: *const CondBr) []*Inst {
- return (self.deaths + self.then_death_count)[0..self.else_death_count];
- }
- };
-
- pub const Constant = struct {
- pub const base_tag = Tag.constant;
-
- base: Inst,
- val: Value,
-
- pub fn operandCount(self: *const Constant) usize {
- return 0;
- }
- pub fn getOperand(self: *const Constant, index: usize) ?*Inst {
- return null;
- }
- };
-
- pub const Loop = struct {
- pub const base_tag = Tag.loop;
-
- base: Inst,
- body: Body,
-
- pub fn operandCount(self: *const Loop) usize {
- return 0;
- }
- pub fn getOperand(self: *const Loop, index: usize) ?*Inst {
- return null;
- }
- };
-
- pub const VarPtr = struct {
- pub const base_tag = Tag.varptr;
-
- base: Inst,
- variable: *Module.Var,
-
- pub fn operandCount(self: *const VarPtr) usize {
- return 0;
- }
- pub fn getOperand(self: *const VarPtr, index: usize) ?*Inst {
- return null;
- }
- };
-};
-
-pub const Body = struct {
- instructions: []*Inst,
-};
diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig
deleted file mode 100644
index fa2ef30ccd13cbc08b6baf3b075e0a2ee20f78da..0000000000000000000000000000000000000000
--- a/src-self-hosted/libc_installation.zig
+++ /dev/null
@@ -1,624 +0,0 @@
-const std = @import("std");
-const builtin = @import("builtin");
-const Target = std.Target;
-const fs = std.fs;
-const Allocator = std.mem.Allocator;
-const Batch = std.event.Batch;
-
-const is_darwin = Target.current.isDarwin();
-const is_windows = Target.current.os.tag == .windows;
-const is_gnu = Target.current.isGnu();
-
-const log = std.log.scoped(.libc_installation);
-
-usingnamespace @import("windows_sdk.zig");
-
-/// See the render function implementation for documentation of the fields.
-pub const LibCInstallation = struct {
- include_dir: ?[]const u8 = null,
- sys_include_dir: ?[]const u8 = null,
- crt_dir: ?[]const u8 = null,
- msvc_lib_dir: ?[]const u8 = null,
- kernel32_lib_dir: ?[]const u8 = null,
-
- pub const FindError = error{
- OutOfMemory,
- FileSystem,
- UnableToSpawnCCompiler,
- CCompilerExitCode,
- CCompilerCrashed,
- CCompilerCannotFindHeaders,
- LibCRuntimeNotFound,
- LibCStdLibHeaderNotFound,
- LibCKernel32LibNotFound,
- UnsupportedArchitecture,
- WindowsSdkNotFound,
- ZigIsTheCCompiler,
- };
-
- pub fn parse(
- allocator: *Allocator,
- libc_file: []const u8,
- ) !LibCInstallation {
- var self: LibCInstallation = .{};
-
- const fields = std.meta.fields(LibCInstallation);
- const FoundKey = struct {
- found: bool,
- allocated: ?[:0]u8,
- };
- var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** fields.len;
- errdefer {
- self = .{};
- for (found_keys) |found_key| {
- if (found_key.allocated) |s| allocator.free(s);
- }
- }
-
- const contents = try std.fs.cwd().readFileAlloc(allocator, libc_file, std.math.maxInt(usize));
- defer allocator.free(contents);
-
- var it = std.mem.tokenize(contents, "\n");
- while (it.next()) |line| {
- if (line.len == 0 or line[0] == '#') continue;
- var line_it = std.mem.split(line, "=");
- const name = line_it.next() orelse {
- log.err("missing equal sign after field name\n", .{});
- return error.ParseError;
- };
- const value = line_it.rest();
- inline for (fields) |field, i| {
- if (std.mem.eql(u8, name, field.name)) {
- found_keys[i].found = true;
- if (value.len == 0) {
- @field(self, field.name) = null;
- } else {
- found_keys[i].allocated = try std.mem.dupeZ(allocator, u8, value);
- @field(self, field.name) = found_keys[i].allocated;
- }
- break;
- }
- }
- }
- inline for (fields) |field, i| {
- if (!found_keys[i].found) {
- log.err("missing field: {}\n", .{field.name});
- return error.ParseError;
- }
- }
- if (self.include_dir == null) {
- log.err("include_dir may not be empty\n", .{});
- return error.ParseError;
- }
- if (self.sys_include_dir == null) {
- log.err("sys_include_dir may not be empty\n", .{});
- return error.ParseError;
- }
- if (self.crt_dir == null and !is_darwin) {
- log.err("crt_dir may not be empty for {}\n", .{@tagName(Target.current.os.tag)});
- return error.ParseError;
- }
- if (self.msvc_lib_dir == null and is_windows and !is_gnu) {
- log.err("msvc_lib_dir may not be empty for {}-{}\n", .{
- @tagName(Target.current.os.tag),
- @tagName(Target.current.abi),
- });
- return error.ParseError;
- }
- if (self.kernel32_lib_dir == null and is_windows and !is_gnu) {
- log.err("kernel32_lib_dir may not be empty for {}-{}\n", .{
- @tagName(Target.current.os.tag),
- @tagName(Target.current.abi),
- });
- return error.ParseError;
- }
-
- return self;
- }
-
- pub fn render(self: LibCInstallation, out: anytype) !void {
- @setEvalBranchQuota(4000);
- const include_dir = self.include_dir orelse "";
- const sys_include_dir = self.sys_include_dir orelse "";
- const crt_dir = self.crt_dir orelse "";
- const msvc_lib_dir = self.msvc_lib_dir orelse "";
- const kernel32_lib_dir = self.kernel32_lib_dir orelse "";
-
- try out.print(
- \\# The directory that contains `stdlib.h`.
- \\# On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null`
- \\include_dir={}
- \\
- \\# The system-specific include directory. May be the same as `include_dir`.
- \\# On Windows it's the directory that includes `vcruntime.h`.
- \\# On POSIX it's the directory that includes `sys/errno.h`.
- \\sys_include_dir={}
- \\
- \\# The directory that contains `crt1.o` or `crt2.o`.
- \\# On POSIX, can be found with `cc -print-file-name=crt1.o`.
- \\# Not needed when targeting MacOS.
- \\crt_dir={}
- \\
- \\# The directory that contains `vcruntime.lib`.
- \\# Only needed when targeting MSVC on Windows.
- \\msvc_lib_dir={}
- \\
- \\# The directory that contains `kernel32.lib`.
- \\# Only needed when targeting MSVC on Windows.
- \\kernel32_lib_dir={}
- \\
- , .{
- include_dir,
- sys_include_dir,
- crt_dir,
- msvc_lib_dir,
- kernel32_lib_dir,
- });
- }
-
- pub const FindNativeOptions = struct {
- allocator: *Allocator,
-
- /// If enabled, will print human-friendly errors to stderr.
- verbose: bool = false,
- };
-
- /// Finds the default, native libc.
- pub fn findNative(args: FindNativeOptions) FindError!LibCInstallation {
- var self: LibCInstallation = .{};
-
- if (is_windows) {
- var sdk: *ZigWindowsSDK = undefined;
- switch (zig_find_windows_sdk(&sdk)) {
- .None => {
- defer zig_free_windows_sdk(sdk);
-
- var batch = Batch(FindError!void, 5, .auto_async).init();
- batch.add(&async self.findNativeMsvcIncludeDir(args, sdk));
- batch.add(&async self.findNativeMsvcLibDir(args, sdk));
- batch.add(&async self.findNativeKernel32LibDir(args, sdk));
- batch.add(&async self.findNativeIncludeDirWindows(args, sdk));
- batch.add(&async self.findNativeCrtDirWindows(args, sdk));
- try batch.wait();
- },
- .OutOfMemory => return error.OutOfMemory,
- .NotFound => return error.WindowsSdkNotFound,
- .PathTooLong => return error.WindowsSdkNotFound,
- }
- } else {
- try blk: {
- var batch = Batch(FindError!void, 2, .auto_async).init();
- errdefer batch.wait() catch {};
- batch.add(&async self.findNativeIncludeDirPosix(args));
- switch (Target.current.os.tag) {
- .freebsd, .netbsd => self.crt_dir = try std.mem.dupeZ(args.allocator, u8, "/usr/lib"),
- .linux, .dragonfly => batch.add(&async self.findNativeCrtDirPosix(args)),
- else => {},
- }
- break :blk batch.wait();
- };
- }
- return self;
- }
-
- /// Must be the same allocator passed to `parse` or `findNative`.
- pub fn deinit(self: *LibCInstallation, allocator: *Allocator) void {
- const fields = std.meta.fields(LibCInstallation);
- inline for (fields) |field| {
- if (@field(self, field.name)) |payload| {
- allocator.free(payload);
- }
- }
- self.* = undefined;
- }
-
- fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
- const allocator = args.allocator;
- const dev_null = if (is_windows) "nul" else "/dev/null";
- const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe;
- const argv = [_][]const u8{
- cc_exe,
- "-E",
- "-Wp,-v",
- "-xc",
- dev_null,
- };
- var env_map = try std.process.getEnvMap(allocator);
- defer env_map.deinit();
-
- // Detect infinite loops.
- const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS";
- if (env_map.get(inf_loop_env_key) != null) return error.ZigIsTheCCompiler;
- try env_map.set(inf_loop_env_key, "1");
-
- const exec_res = std.ChildProcess.exec(.{
- .allocator = allocator,
- .argv = &argv,
- .max_output_bytes = 1024 * 1024,
- .env_map = &env_map,
- // Some C compilers, such as Clang, are known to rely on argv[0] to find the path
- // to their own executable, without even bothering to resolve PATH. This results in the message:
- // error: unable to execute command: Executable "" doesn't exist!
- // So we use the expandArg0 variant of ChildProcess to give them a helping hand.
- .expand_arg0 = .expand,
- }) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- else => {
- printVerboseInvocation(&argv, null, args.verbose, null);
- return error.UnableToSpawnCCompiler;
- },
- };
- defer {
- allocator.free(exec_res.stdout);
- allocator.free(exec_res.stderr);
- }
- switch (exec_res.term) {
- .Exited => |code| if (code != 0) {
- printVerboseInvocation(&argv, null, args.verbose, exec_res.stderr);
- return error.CCompilerExitCode;
- },
- else => {
- printVerboseInvocation(&argv, null, args.verbose, exec_res.stderr);
- return error.CCompilerCrashed;
- },
- }
-
- var it = std.mem.tokenize(exec_res.stderr, "\n\r");
- var search_paths = std.ArrayList([]const u8).init(allocator);
- defer search_paths.deinit();
- while (it.next()) |line| {
- if (line.len != 0 and line[0] == ' ') {
- try search_paths.append(line);
- }
- }
- if (search_paths.items.len == 0) {
- return error.CCompilerCannotFindHeaders;
- }
-
- const include_dir_example_file = "stdlib.h";
- const sys_include_dir_example_file = if (is_windows) "sys\\types.h" else "sys/errno.h";
-
- var path_i: usize = 0;
- while (path_i < search_paths.items.len) : (path_i += 1) {
- // search in reverse order
- const search_path_untrimmed = search_paths.items[search_paths.items.len - path_i - 1];
- const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");
- var search_dir = fs.cwd().openDir(search_path, .{}) catch |err| switch (err) {
- error.FileNotFound,
- error.NotDir,
- error.NoDevice,
- => continue,
-
- else => return error.FileSystem,
- };
- defer search_dir.close();
-
- if (self.include_dir == null) {
- if (search_dir.accessZ(include_dir_example_file, .{})) |_| {
- self.include_dir = try std.mem.dupeZ(allocator, u8, search_path);
- } else |err| switch (err) {
- error.FileNotFound => {},
- else => return error.FileSystem,
- }
- }
-
- if (self.sys_include_dir == null) {
- if (search_dir.accessZ(sys_include_dir_example_file, .{})) |_| {
- self.sys_include_dir = try std.mem.dupeZ(allocator, u8, search_path);
- } else |err| switch (err) {
- error.FileNotFound => {},
- else => return error.FileSystem,
- }
- }
-
- if (self.include_dir != null and self.sys_include_dir != null) {
- // Success.
- return;
- }
- }
-
- return error.LibCStdLibHeaderNotFound;
- }
-
- fn findNativeIncludeDirWindows(
- self: *LibCInstallation,
- args: FindNativeOptions,
- sdk: *ZigWindowsSDK,
- ) FindError!void {
- const allocator = args.allocator;
-
- var search_buf: [2]Search = undefined;
- const searches = fillSearch(&search_buf, sdk);
-
- var result_buf = std.ArrayList(u8).init(allocator);
- defer result_buf.deinit();
-
- for (searches) |search| {
- result_buf.shrink(0);
- try result_buf.outStream().print("{}\\Include\\{}\\ucrt", .{ search.path, search.version });
-
- var dir = fs.cwd().openDir(result_buf.span(), .{}) catch |err| switch (err) {
- error.FileNotFound,
- error.NotDir,
- error.NoDevice,
- => continue,
-
- else => return error.FileSystem,
- };
- defer dir.close();
-
- dir.accessZ("stdlib.h", .{}) catch |err| switch (err) {
- error.FileNotFound => continue,
- else => return error.FileSystem,
- };
-
- self.include_dir = result_buf.toOwnedSlice();
- return;
- }
-
- return error.LibCStdLibHeaderNotFound;
- }
-
- fn findNativeCrtDirWindows(
- self: *LibCInstallation,
- args: FindNativeOptions,
- sdk: *ZigWindowsSDK,
- ) FindError!void {
- const allocator = args.allocator;
-
- var search_buf: [2]Search = undefined;
- const searches = fillSearch(&search_buf, sdk);
-
- var result_buf = std.ArrayList(u8).init(allocator);
- defer result_buf.deinit();
-
- const arch_sub_dir = switch (builtin.arch) {
- .i386 => "x86",
- .x86_64 => "x64",
- .arm, .armeb => "arm",
- else => return error.UnsupportedArchitecture,
- };
-
- for (searches) |search| {
- result_buf.shrink(0);
- try result_buf.outStream().print("{}\\Lib\\{}\\ucrt\\{}", .{ search.path, search.version, arch_sub_dir });
-
- var dir = fs.cwd().openDir(result_buf.span(), .{}) catch |err| switch (err) {
- error.FileNotFound,
- error.NotDir,
- error.NoDevice,
- => continue,
-
- else => return error.FileSystem,
- };
- defer dir.close();
-
- dir.accessZ("ucrt.lib", .{}) catch |err| switch (err) {
- error.FileNotFound => continue,
- else => return error.FileSystem,
- };
-
- self.crt_dir = result_buf.toOwnedSlice();
- return;
- }
- return error.LibCRuntimeNotFound;
- }
-
- fn findNativeCrtDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
- self.crt_dir = try ccPrintFileName(.{
- .allocator = args.allocator,
- .search_basename = "crt1.o",
- .want_dirname = .only_dir,
- .verbose = args.verbose,
- });
- }
-
- fn findNativeKernel32LibDir(
- self: *LibCInstallation,
- args: FindNativeOptions,
- sdk: *ZigWindowsSDK,
- ) FindError!void {
- const allocator = args.allocator;
-
- var search_buf: [2]Search = undefined;
- const searches = fillSearch(&search_buf, sdk);
-
- var result_buf = std.ArrayList(u8).init(allocator);
- defer result_buf.deinit();
-
- const arch_sub_dir = switch (builtin.arch) {
- .i386 => "x86",
- .x86_64 => "x64",
- .arm, .armeb => "arm",
- else => return error.UnsupportedArchitecture,
- };
-
- for (searches) |search| {
- result_buf.shrink(0);
- const stream = result_buf.outStream();
- try stream.print("{}\\Lib\\{}\\um\\{}", .{ search.path, search.version, arch_sub_dir });
-
- var dir = fs.cwd().openDir(result_buf.span(), .{}) catch |err| switch (err) {
- error.FileNotFound,
- error.NotDir,
- error.NoDevice,
- => continue,
-
- else => return error.FileSystem,
- };
- defer dir.close();
-
- dir.accessZ("kernel32.lib", .{}) catch |err| switch (err) {
- error.FileNotFound => continue,
- else => return error.FileSystem,
- };
-
- self.kernel32_lib_dir = result_buf.toOwnedSlice();
- return;
- }
- return error.LibCKernel32LibNotFound;
- }
-
- fn findNativeMsvcIncludeDir(
- self: *LibCInstallation,
- args: FindNativeOptions,
- sdk: *ZigWindowsSDK,
- ) FindError!void {
- const allocator = args.allocator;
-
- const msvc_lib_dir_ptr = sdk.msvc_lib_dir_ptr orelse return error.LibCStdLibHeaderNotFound;
- const msvc_lib_dir = msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len];
- const up1 = fs.path.dirname(msvc_lib_dir) orelse return error.LibCStdLibHeaderNotFound;
- const up2 = fs.path.dirname(up1) orelse return error.LibCStdLibHeaderNotFound;
-
- const dir_path = try fs.path.join(allocator, &[_][]const u8{ up2, "include" });
- errdefer allocator.free(dir_path);
-
- var dir = fs.cwd().openDir(dir_path, .{}) catch |err| switch (err) {
- error.FileNotFound,
- error.NotDir,
- error.NoDevice,
- => return error.LibCStdLibHeaderNotFound,
-
- else => return error.FileSystem,
- };
- defer dir.close();
-
- dir.accessZ("vcruntime.h", .{}) catch |err| switch (err) {
- error.FileNotFound => return error.LibCStdLibHeaderNotFound,
- else => return error.FileSystem,
- };
-
- self.sys_include_dir = dir_path;
- }
-
- fn findNativeMsvcLibDir(
- self: *LibCInstallation,
- args: FindNativeOptions,
- sdk: *ZigWindowsSDK,
- ) FindError!void {
- const allocator = args.allocator;
- const msvc_lib_dir_ptr = sdk.msvc_lib_dir_ptr orelse return error.LibCRuntimeNotFound;
- self.msvc_lib_dir = try std.mem.dupeZ(allocator, u8, msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len]);
- }
-};
-
-const default_cc_exe = if (is_windows) "cc.exe" else "cc";
-
-pub const CCPrintFileNameOptions = struct {
- allocator: *Allocator,
- search_basename: []const u8,
- want_dirname: enum { full_path, only_dir },
- verbose: bool = false,
-};
-
-/// caller owns returned memory
-fn ccPrintFileName(args: CCPrintFileNameOptions) ![:0]u8 {
- const allocator = args.allocator;
-
- const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe;
- const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={}", .{args.search_basename});
- defer allocator.free(arg1);
- const argv = [_][]const u8{ cc_exe, arg1 };
-
- var env_map = try std.process.getEnvMap(allocator);
- defer env_map.deinit();
-
- // Detect infinite loops.
- const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS";
- if (env_map.get(inf_loop_env_key) != null) return error.ZigIsTheCCompiler;
- try env_map.set(inf_loop_env_key, "1");
-
- const exec_res = std.ChildProcess.exec(.{
- .allocator = allocator,
- .argv = &argv,
- .max_output_bytes = 1024 * 1024,
- .env_map = &env_map,
- // Some C compilers, such as Clang, are known to rely on argv[0] to find the path
- // to their own executable, without even bothering to resolve PATH. This results in the message:
- // error: unable to execute command: Executable "" doesn't exist!
- // So we use the expandArg0 variant of ChildProcess to give them a helping hand.
- .expand_arg0 = .expand,
- }) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- else => return error.UnableToSpawnCCompiler,
- };
- defer {
- allocator.free(exec_res.stdout);
- allocator.free(exec_res.stderr);
- }
- switch (exec_res.term) {
- .Exited => |code| if (code != 0) {
- printVerboseInvocation(&argv, args.search_basename, args.verbose, exec_res.stderr);
- return error.CCompilerExitCode;
- },
- else => {
- printVerboseInvocation(&argv, args.search_basename, args.verbose, exec_res.stderr);
- return error.CCompilerCrashed;
- },
- }
-
- var it = std.mem.tokenize(exec_res.stdout, "\n\r");
- const line = it.next() orelse return error.LibCRuntimeNotFound;
- // When this command fails, it returns exit code 0 and duplicates the input file name.
- // So we detect failure by checking if the output matches exactly the input.
- if (std.mem.eql(u8, line, args.search_basename)) return error.LibCRuntimeNotFound;
- switch (args.want_dirname) {
- .full_path => return std.mem.dupeZ(allocator, u8, line),
- .only_dir => {
- const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound;
- return std.mem.dupeZ(allocator, u8, dirname);
- },
- }
-}
-
-fn printVerboseInvocation(
- argv: []const []const u8,
- search_basename: ?[]const u8,
- verbose: bool,
- stderr: ?[]const u8,
-) void {
- if (!verbose) return;
-
- if (search_basename) |s| {
- std.debug.warn("Zig attempted to find the file '{}' by executing this command:\n", .{s});
- } else {
- std.debug.warn("Zig attempted to find the path to native system libc headers by executing this command:\n", .{});
- }
- for (argv) |arg, i| {
- if (i != 0) std.debug.warn(" ", .{});
- std.debug.warn("{}", .{arg});
- }
- std.debug.warn("\n", .{});
- if (stderr) |s| {
- std.debug.warn("Output:\n==========\n{}\n==========\n", .{s});
- }
-}
-
-const Search = struct {
- path: []const u8,
- version: []const u8,
-};
-
-fn fillSearch(search_buf: *[2]Search, sdk: *ZigWindowsSDK) []Search {
- var search_end: usize = 0;
- if (sdk.path10_ptr) |path10_ptr| {
- if (sdk.version10_ptr) |version10_ptr| {
- search_buf[search_end] = Search{
- .path = path10_ptr[0..sdk.path10_len],
- .version = version10_ptr[0..sdk.version10_len],
- };
- search_end += 1;
- }
- }
- if (sdk.path81_ptr) |path81_ptr| {
- if (sdk.version81_ptr) |version81_ptr| {
- search_buf[search_end] = Search{
- .path = path81_ptr[0..sdk.path81_len],
- .version = version81_ptr[0..sdk.version81_len],
- };
- search_end += 1;
- }
- }
- return search_buf[0..search_end];
-}
diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig
deleted file mode 100644
index fff69a6bbd10bc077857a3e5c999d2da4504012e..0000000000000000000000000000000000000000
--- 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/C.zig b/src-self-hosted/link/C.zig
deleted file mode 100644
index 69eabd1f8bb8b50b7cdfcb94edc97753c0dad4b1..0000000000000000000000000000000000000000
--- a/src-self-hosted/link/C.zig
+++ /dev/null
@@ -1,101 +0,0 @@
-const std = @import("std");
-const mem = std.mem;
-const assert = std.debug.assert;
-const Allocator = std.mem.Allocator;
-const Module = @import("../Module.zig");
-const fs = std.fs;
-const codegen = @import("../codegen/c.zig");
-const link = @import("../link.zig");
-const File = link.File;
-const C = @This();
-
-pub const base_tag: File.Tag = .c;
-
-base: File,
-
-header: std.ArrayList(u8),
-constants: std.ArrayList(u8),
-main: std.ArrayList(u8),
-
-called: std.StringHashMap(void),
-need_stddef: bool = false,
-need_stdint: bool = false,
-error_msg: *Module.ErrorMsg = undefined,
-
-pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File {
- assert(options.object_format == .c);
-
- const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.determineMode(options) });
- errdefer file.close();
-
- var c_file = try allocator.create(C);
- errdefer allocator.destroy(c_file);
-
- c_file.* = C{
- .base = .{
- .tag = .c,
- .options = options,
- .file = file,
- .allocator = allocator,
- },
- .main = std.ArrayList(u8).init(allocator),
- .header = std.ArrayList(u8).init(allocator),
- .constants = std.ArrayList(u8).init(allocator),
- .called = std.StringHashMap(void).init(allocator),
- };
-
- return &c_file.base;
-}
-
-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);
- return error.AnalysisFail;
-}
-
-pub fn deinit(self: *C) void {
- self.main.deinit();
- self.header.deinit();
- self.constants.deinit();
- self.called.deinit();
-}
-
-pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
- codegen.generate(self, decl) catch |err| {
- if (err == error.AnalysisFail) {
- try module.failed_decls.put(module.gpa, decl, self.error_msg);
- }
- return err;
- };
-}
-
-pub fn flush(self: *C, module: *Module) !void {
- const writer = self.base.file.?.writer();
- try writer.writeAll(@embedFile("cbe.h"));
- var includes = false;
- if (self.need_stddef) {
- try writer.writeAll("#include \n");
- includes = true;
- }
- if (self.need_stdint) {
- try writer.writeAll("#include \n");
- includes = true;
- }
- if (includes) {
- try writer.writeByte('\n');
- }
- if (self.header.items.len > 0) {
- try writer.print("{}\n", .{self.header.items});
- }
- if (self.constants.items.len > 0) {
- try writer.print("{}\n", .{self.constants.items});
- }
- if (self.main.items.len > 1) {
- const last_two = self.main.items[self.main.items.len - 2 ..];
- if (std.mem.eql(u8, last_two, "\n\n")) {
- self.main.items.len -= 1;
- }
- }
- try writer.writeAll(self.main.items);
- self.base.file.?.close();
- self.base.file = null;
-}
diff --git a/src-self-hosted/link/Coff.zig b/src-self-hosted/link/Coff.zig
deleted file mode 100644
index 4d1f95e567e3542d7c7230a05a2a1b6b7b411996..0000000000000000000000000000000000000000
--- 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/Elf.zig b/src-self-hosted/link/Elf.zig
deleted file mode 100644
index e5acde947c479c83a22ecf147869ec0fce4c0274..0000000000000000000000000000000000000000
--- a/src-self-hosted/link/Elf.zig
+++ /dev/null
@@ -1,2632 +0,0 @@
-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 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 default_entry_addr = 0x8000000;
-
-// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
-// zig fmt: off
-
-pub const base_tag: File.Tag = .elf;
-
-base: File,
-
-ptr_width: enum { p32, p64 },
-
-/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
-/// Same order as in the file.
-sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},
-shdr_table_offset: ?u64 = null,
-
-/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
-/// Same order as in the file.
-program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){},
-phdr_table_offset: ?u64 = null,
-/// The index into the program headers of a PT_LOAD program header with Read and Execute flags
-phdr_load_re_index: ?u16 = null,
-/// The index into the program headers of the global offset table.
-/// It needs PT_LOAD and Read flags.
-phdr_got_index: ?u16 = null,
-entry_addr: ?u64 = null,
-
-debug_strtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
-shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
-shstrtab_index: ?u16 = null,
-
-text_section_index: ?u16 = null,
-symtab_section_index: ?u16 = null,
-got_section_index: ?u16 = null,
-debug_info_section_index: ?u16 = null,
-debug_abbrev_section_index: ?u16 = null,
-debug_str_section_index: ?u16 = null,
-debug_aranges_section_index: ?u16 = null,
-debug_line_section_index: ?u16 = null,
-
-debug_abbrev_table_offset: ?u64 = null,
-
-/// The same order as in the file. ELF requires global symbols to all be after the
-/// local symbols, they cannot be mixed. So we must buffer all the global symbols and
-/// write them at the end. These are only the local symbols. The length of this array
-/// is the value used for sh_info in the .symtab section.
-local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
-global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
-
-local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
-global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
-offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
-
-/// Same order as in the file. The value is the absolute vaddr value.
-/// If the vaddr of the executable program header changes, the entire
-/// offset table needs to be rewritten.
-offset_table: std.ArrayListUnmanaged(u64) = .{},
-
-phdr_table_dirty: bool = false,
-shdr_table_dirty: bool = false,
-shstrtab_dirty: bool = false,
-debug_strtab_dirty: bool = false,
-offset_table_count_dirty: bool = false,
-debug_abbrev_section_dirty: bool = false,
-debug_aranges_section_dirty: bool = false,
-
-debug_info_header_dirty: bool = false,
-debug_line_header_dirty: bool = false,
-
-error_flags: File.ErrorFlags = File.ErrorFlags{},
-
-/// A list of text blocks that have surplus capacity. This list can have false
-/// positives, as functions grow and shrink over time, only sometimes being added
-/// or removed from the freelist.
-///
-/// A text block has surplus capacity when its overcapacity value is greater than
-/// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so
-/// much extra capacity, that we could fit a small new symbol in it, itself with
-/// ideal_capacity or more.
-///
-/// Ideal capacity is defined by size * alloc_num / alloc_den.
-///
-/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
-/// overcapacity can be negative. A simple way to have negative overcapacity is to
-/// allocate a fresh text block, which will have ideal capacity, and then grow it
-/// by 1 byte. It will then have -1 overcapacity.
-text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
-last_text_block: ?*TextBlock = null,
-
-/// A list of `SrcFn` whose Line Number Programs have surplus capacity.
-/// This is the same concept as `text_block_free_list`; see those doc comments.
-dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{},
-dbg_line_fn_first: ?*SrcFn = null,
-dbg_line_fn_last: ?*SrcFn = null,
-
-/// A list of `TextBlock` whose corresponding .debug_info tags have surplus capacity.
-/// This is the same concept as `text_block_free_list`; see those doc comments.
-dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*TextBlock, void) = .{},
-dbg_info_decl_first: ?*TextBlock = null,
-dbg_info_decl_last: ?*TextBlock = null,
-
-/// `alloc_num / alloc_den` is the factor of padding when allocating.
-const alloc_num = 4;
-const alloc_den = 3;
-
-/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
-/// it as a possible place to put new symbols, it must have enough room for this many bytes
-/// (plus extra for reserved capacity).
-const minimum_text_block_size = 64;
-const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;
-
-pub const TextBlock = struct {
- /// Each decl always gets a local symbol with the fully qualified name.
- /// The vaddr and size are found here directly.
- /// The file offset is found by computing the vaddr offset from the section vaddr
- /// the symbol references, and adding that to the file offset of the section.
- /// If this field is 0, it means the codegen size = 0 and there is no symbol or
- /// offset table entry.
- local_sym_index: 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,
-
- /// Previous/next linked list pointers. This value is `next ^ prev`.
- /// This is the linked list node for this Decl's corresponding .debug_info tag.
- dbg_info_prev: ?*TextBlock,
- dbg_info_next: ?*TextBlock,
- /// Offset into .debug_info pointing to the tag for this Decl.
- dbg_info_off: u32,
- /// Size of the .debug_info tag for this Decl, not including padding.
- dbg_info_len: u32,
-
- pub const empty = TextBlock{
- .local_sym_index = 0,
- .offset_table_index = undefined,
- .prev = null,
- .next = null,
- .dbg_info_prev = null,
- .dbg_info_next = null,
- .dbg_info_off = undefined,
- .dbg_info_len = undefined,
- };
-
- /// Returns how much room there is to grow in virtual address space.
- /// File offset relocation happens transparently, so it is not included in
- /// this calculation.
- fn capacity(self: TextBlock, elf_file: Elf) u64 {
- const self_sym = elf_file.local_symbols.items[self.local_sym_index];
- if (self.next) |next| {
- const next_sym = elf_file.local_symbols.items[next.local_sym_index];
- return next_sym.st_value - self_sym.st_value;
- } else {
- // We are the last block. The capacity is limited only by virtual address space.
- return std.math.maxInt(u32) - self_sym.st_value;
- }
- }
-
- fn freeListEligible(self: TextBlock, elf_file: Elf) bool {
- // No need to keep a free list node for the last block.
- const next = self.next orelse return false;
- const self_sym = elf_file.local_symbols.items[self.local_sym_index];
- const next_sym = elf_file.local_symbols.items[next.local_sym_index];
- const cap = next_sym.st_value - self_sym.st_value;
- const ideal_cap = self_sym.st_size * alloc_num / alloc_den;
- if (cap <= ideal_cap) return false;
- const surplus = cap - ideal_cap;
- return surplus >= min_text_capacity;
- }
-};
-
-pub const Export = struct {
- sym_index: ?u32 = null,
-};
-
-pub const SrcFn = struct {
- /// Offset from the beginning of the Debug Line Program header that contains this function.
- off: u32,
- /// Size of the line number program component belonging to this function, not
- /// including padding.
- len: u32,
-
- /// Points to the previous and next neighbors, based on the offset from .debug_line.
- /// This can be used to find, for example, the capacity of this `SrcFn`.
- prev: ?*SrcFn,
- next: ?*SrcFn,
-
- pub const empty: SrcFn = .{
- .off = 0,
- .len = 0,
- .prev = null,
- .next = null,
- };
-};
-
-pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File {
- assert(options.object_format == .elf);
-
- const file = try dir.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);
-
- 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();
-
- // Index 0 is always a null symbol.
- try self.local_symbols.append(allocator, .{
- .st_name = 0,
- .st_info = 0,
- .st_other = 0,
- .st_shndx = 0,
- .st_value = 0,
- .st_size = 0,
- });
-
- // There must always be a null section in index 0
- try self.sections.append(allocator, .{
- .sh_name = 0,
- .sh_type = elf.SHT_NULL,
- .sh_flags = 0,
- .sh_addr = 0,
- .sh_offset = 0,
- .sh_size = 0,
- .sh_link = 0,
- .sh_info = 0,
- .sh_addralign = 0,
- .sh_entsize = 0,
- });
-
- try self.populateMissingMetadata();
-
- return self;
-}
-
-pub fn deinit(self: *Elf) void {
- self.sections.deinit(self.base.allocator);
- self.program_headers.deinit(self.base.allocator);
- self.shstrtab.deinit(self.base.allocator);
- self.debug_strtab.deinit(self.base.allocator);
- self.local_symbols.deinit(self.base.allocator);
- self.global_symbols.deinit(self.base.allocator);
- self.global_symbol_free_list.deinit(self.base.allocator);
- self.local_symbol_free_list.deinit(self.base.allocator);
- self.offset_table_free_list.deinit(self.base.allocator);
- self.text_block_free_list.deinit(self.base.allocator);
- self.dbg_line_fn_free_list.deinit(self.base.allocator);
- self.dbg_info_decl_free_list.deinit(self.base.allocator);
- self.offset_table.deinit(self.base.allocator);
-}
-
-pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl) u64 {
- assert(decl.link.elf.local_sym_index != 0);
- return self.local_symbols.items[decl.link.elf.local_sym_index].st_value;
-}
-
-fn getDebugLineProgramOff(self: Elf) u32 {
- return self.dbg_line_fn_first.?.off;
-}
-
-fn getDebugLineProgramEnd(self: Elf) u32 {
- return self.dbg_line_fn_last.?.off + self.dbg_line_fn_last.?.len;
-}
-
-/// Returns end pos of collision, if any.
-fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
- const small_ptr = self.ptr_width == .p32;
- const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
- if (start < ehdr_size)
- return ehdr_size;
-
- const end = start + satMul(size, alloc_num) / alloc_den;
-
- if (self.shdr_table_offset) |off| {
- const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
- const tight_size = self.sections.items.len * shdr_size;
- const increased_size = satMul(tight_size, alloc_num) / alloc_den;
- const test_end = off + increased_size;
- if (end > off and start < test_end) {
- return test_end;
- }
- }
-
- if (self.phdr_table_offset) |off| {
- const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
- const tight_size = self.sections.items.len * phdr_size;
- const increased_size = satMul(tight_size, alloc_num) / alloc_den;
- const test_end = off + increased_size;
- if (end > off and start < test_end) {
- return test_end;
- }
- }
-
- for (self.sections.items) |section| {
- const increased_size = satMul(section.sh_size, alloc_num) / alloc_den;
- const test_end = section.sh_offset + increased_size;
- if (end > section.sh_offset and start < test_end) {
- return test_end;
- }
- }
- for (self.program_headers.items) |program_header| {
- const increased_size = satMul(program_header.p_filesz, alloc_num) / alloc_den;
- const test_end = program_header.p_offset + increased_size;
- if (end > program_header.p_offset and start < test_end) {
- return test_end;
- }
- }
- return null;
-}
-
-fn allocatedSize(self: *Elf, start: u64) u64 {
- if (start == 0)
- return 0;
- var min_pos: u64 = std.math.maxInt(u64);
- if (self.shdr_table_offset) |off| {
- if (off > start and off < min_pos) min_pos = off;
- }
- if (self.phdr_table_offset) |off| {
- if (off > start and off < min_pos) min_pos = off;
- }
- for (self.sections.items) |section| {
- if (section.sh_offset <= start) continue;
- if (section.sh_offset < min_pos) min_pos = section.sh_offset;
- }
- for (self.program_headers.items) |program_header| {
- if (program_header.p_offset <= start) continue;
- if (program_header.p_offset < min_pos) min_pos = program_header.p_offset;
- }
- return min_pos - start;
-}
-
-fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 {
- var start: u64 = 0;
- while (self.detectAllocCollision(start, object_size)) |item_end| {
- start = mem.alignForwardGeneric(u64, item_end, min_alignment);
- }
- return start;
-}
-
-/// TODO Improve this to use a table.
-fn makeString(self: *Elf, bytes: []const u8) !u32 {
- try self.shstrtab.ensureCapacity(self.base.allocator, self.shstrtab.items.len + bytes.len + 1);
- const result = self.shstrtab.items.len;
- self.shstrtab.appendSliceAssumeCapacity(bytes);
- self.shstrtab.appendAssumeCapacity(0);
- return @intCast(u32, result);
-}
-
-/// TODO Improve this to use a table.
-fn makeDebugString(self: *Elf, bytes: []const u8) !u32 {
- try self.debug_strtab.ensureCapacity(self.base.allocator, self.debug_strtab.items.len + bytes.len + 1);
- const result = self.debug_strtab.items.len;
- self.debug_strtab.appendSliceAssumeCapacity(bytes);
- self.debug_strtab.appendAssumeCapacity(0);
- return @intCast(u32, result);
-}
-
-fn getString(self: *Elf, str_off: u32) []const u8 {
- assert(str_off < self.shstrtab.items.len);
- return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));
-}
-
-fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 {
- const existing_name = self.getString(old_str_off);
- if (mem.eql(u8, existing_name, new_name)) {
- return old_str_off;
- }
- return self.makeString(new_name);
-}
-
-pub fn populateMissingMetadata(self: *Elf) !void {
- const small_ptr = switch (self.ptr_width) {
- .p32 => true,
- .p64 => false,
- };
- const ptr_size: u8 = self.ptrWidthBytes();
- if (self.phdr_load_re_index == null) {
- self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
- const file_size = self.base.options.program_code_size_hint;
- const p_align = 0x1000;
- const off = self.findFreeSpace(file_size, p_align);
- log.debug("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
- const entry_addr: u64 = self.entry_addr orelse if (self.base.options.target.cpu.arch == .spu_2) @as(u64, 0) else default_entry_addr;
- try self.program_headers.append(self.base.allocator, .{
- .p_type = elf.PT_LOAD,
- .p_offset = off,
- .p_filesz = file_size,
- .p_vaddr = entry_addr,
- .p_paddr = entry_addr,
- .p_memsz = file_size,
- .p_align = p_align,
- .p_flags = elf.PF_X | elf.PF_R,
- });
- self.entry_addr = null;
- self.phdr_table_dirty = true;
- }
- if (self.phdr_got_index == null) {
- self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
- const file_size = @as(u64, ptr_size) * self.base.options.symbol_count_hint;
- // We really only need ptr alignment but since we are using PROGBITS, linux requires
- // page align.
- const p_align = if (self.base.options.target.os.tag == .linux) 0x1000 else @as(u16, ptr_size);
- const off = self.findFreeSpace(file_size, p_align);
- log.debug("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
- // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
- // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
- // else in virtual memory.
- const got_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x4000000 else 0x8000;
- try self.program_headers.append(self.base.allocator, .{
- .p_type = elf.PT_LOAD,
- .p_offset = off,
- .p_filesz = file_size,
- .p_vaddr = got_addr,
- .p_paddr = got_addr,
- .p_memsz = file_size,
- .p_align = p_align,
- .p_flags = elf.PF_R,
- });
- self.phdr_table_dirty = true;
- }
- if (self.shstrtab_index == null) {
- self.shstrtab_index = @intCast(u16, self.sections.items.len);
- assert(self.shstrtab.items.len == 0);
- try self.shstrtab.append(self.base.allocator, 0); // need a 0 at position 0
- const off = self.findFreeSpace(self.shstrtab.items.len, 1);
- log.debug("found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
- try self.sections.append(self.base.allocator, .{
- .sh_name = try self.makeString(".shstrtab"),
- .sh_type = elf.SHT_STRTAB,
- .sh_flags = 0,
- .sh_addr = 0,
- .sh_offset = off,
- .sh_size = self.shstrtab.items.len,
- .sh_link = 0,
- .sh_info = 0,
- .sh_addralign = 1,
- .sh_entsize = 0,
- });
- self.shstrtab_dirty = true;
- self.shdr_table_dirty = true;
- }
- if (self.text_section_index == null) {
- self.text_section_index = @intCast(u16, self.sections.items.len);
- const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
-
- try self.sections.append(self.base.allocator, .{
- .sh_name = try self.makeString(".text"),
- .sh_type = elf.SHT_PROGBITS,
- .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
- .sh_addr = phdr.p_vaddr,
- .sh_offset = phdr.p_offset,
- .sh_size = phdr.p_filesz,
- .sh_link = 0,
- .sh_info = 0,
- .sh_addralign = phdr.p_align,
- .sh_entsize = 0,
- });
- self.shdr_table_dirty = true;
- }
- if (self.got_section_index == null) {
- self.got_section_index = @intCast(u16, self.sections.items.len);
- const phdr = &self.program_headers.items[self.phdr_got_index.?];
-
- try self.sections.append(self.base.allocator, .{
- .sh_name = try self.makeString(".got"),
- .sh_type = elf.SHT_PROGBITS,
- .sh_flags = elf.SHF_ALLOC,
- .sh_addr = phdr.p_vaddr,
- .sh_offset = phdr.p_offset,
- .sh_size = phdr.p_filesz,
- .sh_link = 0,
- .sh_info = 0,
- .sh_addralign = phdr.p_align,
- .sh_entsize = 0,
- });
- self.shdr_table_dirty = true;
- }
- if (self.symtab_section_index == null) {
- self.symtab_section_index = @intCast(u16, self.sections.items.len);
- const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
- const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
- const file_size = self.base.options.symbol_count_hint * each_size;
- const off = self.findFreeSpace(file_size, min_align);
- log.debug("found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
-
- try self.sections.append(self.base.allocator, .{
- .sh_name = try self.makeString(".symtab"),
- .sh_type = elf.SHT_SYMTAB,
- .sh_flags = 0,
- .sh_addr = 0,
- .sh_offset = off,
- .sh_size = file_size,
- // The section header index of the associated string table.
- .sh_link = self.shstrtab_index.?,
- .sh_info = @intCast(u32, self.local_symbols.items.len),
- .sh_addralign = min_align,
- .sh_entsize = each_size,
- });
- self.shdr_table_dirty = true;
- try self.writeSymbol(0);
- }
- if (self.debug_str_section_index == null) {
- self.debug_str_section_index = @intCast(u16, self.sections.items.len);
- assert(self.debug_strtab.items.len == 0);
- try self.sections.append(self.base.allocator, .{
- .sh_name = try self.makeString(".debug_str"),
- .sh_type = elf.SHT_PROGBITS,
- .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,
- .sh_addr = 0,
- .sh_offset = 0,
- .sh_size = self.debug_strtab.items.len,
- .sh_link = 0,
- .sh_info = 0,
- .sh_addralign = 1,
- .sh_entsize = 1,
- });
- self.debug_strtab_dirty = true;
- self.shdr_table_dirty = true;
- }
- if (self.debug_info_section_index == null) {
- self.debug_info_section_index = @intCast(u16, self.sections.items.len);
-
- const file_size_hint = 200;
- const p_align = 1;
- const off = self.findFreeSpace(file_size_hint, p_align);
- log.debug("found .debug_info free space 0x{x} to 0x{x}\n", .{
- off,
- off + file_size_hint,
- });
- try self.sections.append(self.base.allocator, .{
- .sh_name = try self.makeString(".debug_info"),
- .sh_type = elf.SHT_PROGBITS,
- .sh_flags = 0,
- .sh_addr = 0,
- .sh_offset = off,
- .sh_size = file_size_hint,
- .sh_link = 0,
- .sh_info = 0,
- .sh_addralign = p_align,
- .sh_entsize = 0,
- });
- self.shdr_table_dirty = true;
- self.debug_info_header_dirty = true;
- }
- if (self.debug_abbrev_section_index == null) {
- self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len);
-
- const file_size_hint = 128;
- const p_align = 1;
- const off = self.findFreeSpace(file_size_hint, p_align);
- log.debug("found .debug_abbrev free space 0x{x} to 0x{x}\n", .{
- off,
- off + file_size_hint,
- });
- try self.sections.append(self.base.allocator, .{
- .sh_name = try self.makeString(".debug_abbrev"),
- .sh_type = elf.SHT_PROGBITS,
- .sh_flags = 0,
- .sh_addr = 0,
- .sh_offset = off,
- .sh_size = file_size_hint,
- .sh_link = 0,
- .sh_info = 0,
- .sh_addralign = p_align,
- .sh_entsize = 0,
- });
- self.shdr_table_dirty = true;
- self.debug_abbrev_section_dirty = true;
- }
- if (self.debug_aranges_section_index == null) {
- self.debug_aranges_section_index = @intCast(u16, self.sections.items.len);
-
- const file_size_hint = 160;
- const p_align = 16;
- const off = self.findFreeSpace(file_size_hint, p_align);
- log.debug("found .debug_aranges free space 0x{x} to 0x{x}\n", .{
- off,
- off + file_size_hint,
- });
- try self.sections.append(self.base.allocator, .{
- .sh_name = try self.makeString(".debug_aranges"),
- .sh_type = elf.SHT_PROGBITS,
- .sh_flags = 0,
- .sh_addr = 0,
- .sh_offset = off,
- .sh_size = file_size_hint,
- .sh_link = 0,
- .sh_info = 0,
- .sh_addralign = p_align,
- .sh_entsize = 0,
- });
- self.shdr_table_dirty = true;
- self.debug_aranges_section_dirty = true;
- }
- if (self.debug_line_section_index == null) {
- self.debug_line_section_index = @intCast(u16, self.sections.items.len);
-
- const file_size_hint = 250;
- const p_align = 1;
- const off = self.findFreeSpace(file_size_hint, p_align);
- log.debug("found .debug_line free space 0x{x} to 0x{x}\n", .{
- off,
- off + file_size_hint,
- });
- try self.sections.append(self.base.allocator, .{
- .sh_name = try self.makeString(".debug_line"),
- .sh_type = elf.SHT_PROGBITS,
- .sh_flags = 0,
- .sh_addr = 0,
- .sh_offset = off,
- .sh_size = file_size_hint,
- .sh_link = 0,
- .sh_info = 0,
- .sh_addralign = p_align,
- .sh_entsize = 0,
- });
- self.shdr_table_dirty = true;
- self.debug_line_header_dirty = true;
- }
- const shsize: u64 = switch (self.ptr_width) {
- .p32 => @sizeOf(elf.Elf32_Shdr),
- .p64 => @sizeOf(elf.Elf64_Shdr),
- };
- const shalign: u16 = switch (self.ptr_width) {
- .p32 => @alignOf(elf.Elf32_Shdr),
- .p64 => @alignOf(elf.Elf64_Shdr),
- };
- if (self.shdr_table_offset == null) {
- self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);
- self.shdr_table_dirty = true;
- }
- const phsize: u64 = switch (self.ptr_width) {
- .p32 => @sizeOf(elf.Elf32_Phdr),
- .p64 => @sizeOf(elf.Elf64_Phdr),
- };
- const phalign: u16 = switch (self.ptr_width) {
- .p32 => @alignOf(elf.Elf32_Phdr),
- .p64 => @alignOf(elf.Elf64_Phdr),
- };
- if (self.phdr_table_offset == null) {
- self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
- self.phdr_table_dirty = true;
- }
- {
- // Iterate over symbols, populating free_list and last_text_block.
- if (self.local_symbols.items.len != 1) {
- @panic("TODO implement setting up free_list and last_text_block from existing ELF file");
- }
- // We are starting with an empty file. The default values are correct, null and empty list.
- }
-}
-
-pub const abbrev_compile_unit = 1;
-pub const abbrev_subprogram = 2;
-pub const abbrev_subprogram_retvoid = 3;
-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 {
- 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();
- const init_len_size: usize = switch (self.ptr_width) {
- .p32 => 4,
- .p64 => 12,
- };
-
- // Unfortunately these have to be buffered and done at the end because ELF does not allow
- // mixing local and global symbols within a symbol table.
- try self.writeAllGlobalSymbols();
-
- if (self.debug_abbrev_section_dirty) {
- const debug_abbrev_sect = &self.sections.items[self.debug_abbrev_section_index.?];
-
- // These are LEB encoded but since the values are all less than 127
- // we can simply append these bytes.
- const abbrev_buf = [_]u8{
- abbrev_compile_unit, DW.TAG_compile_unit, DW.CHILDREN_yes, // header
- DW.AT_stmt_list, DW.FORM_sec_offset, 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, // table sentinel
- abbrev_subprogram, DW.TAG_subprogram,
- DW.CHILDREN_yes, // header
- DW.AT_low_pc, DW.FORM_addr,
- DW.AT_high_pc, DW.FORM_data4, DW.AT_type,
- DW.FORM_ref4, DW.AT_name, DW.FORM_string,
- 0, 0, // table sentinel
- abbrev_subprogram_retvoid,
- DW.TAG_subprogram, DW.CHILDREN_yes, // header
- DW.AT_low_pc,
- DW.FORM_addr, DW.AT_high_pc, DW.FORM_data4,
- DW.AT_name, DW.FORM_string, 0,
- 0, // table sentinel
- abbrev_base_type, DW.TAG_base_type,
- DW.CHILDREN_no, // header
- DW.AT_encoding, DW.FORM_data1,
- DW.AT_byte_size, DW.FORM_data1, DW.AT_name,
- DW.FORM_string, 0, 0, // table sentinel
-
- abbrev_pad1, DW.TAG_unspecified_type, DW.CHILDREN_no, // header
- 0, 0, // table sentinel
- abbrev_parameter,
- DW.TAG_formal_parameter, DW.CHILDREN_no, // header
- DW.AT_location,
- DW.FORM_exprloc, DW.AT_type, DW.FORM_ref4,
- DW.AT_name, DW.FORM_string, 0,
- 0, // table sentinel
- 0, 0,
- 0, // section sentinel
- };
-
- const needed_size = abbrev_buf.len;
- const allocated_size = self.allocatedSize(debug_abbrev_sect.sh_offset);
- if (needed_size > allocated_size) {
- debug_abbrev_sect.sh_size = 0; // free the space
- debug_abbrev_sect.sh_offset = self.findFreeSpace(needed_size, 1);
- }
- debug_abbrev_sect.sh_size = needed_size;
- log.debug(".debug_abbrev start=0x{x} end=0x{x}\n", .{
- debug_abbrev_sect.sh_offset,
- debug_abbrev_sect.sh_offset + needed_size,
- });
-
- const abbrev_offset = 0;
- self.debug_abbrev_table_offset = abbrev_offset;
- try self.base.file.?.pwriteAll(&abbrev_buf, debug_abbrev_sect.sh_offset + abbrev_offset);
- if (!self.shdr_table_dirty) {
- // Then it won't get written with the others and we need to do it.
- try self.writeSectHeader(self.debug_abbrev_section_index.?);
- }
-
- self.debug_abbrev_section_dirty = false;
- }
-
- if (self.debug_info_header_dirty) debug_info: {
- // If this value is null it means there is an error in the module;
- // leave debug_info_header_dirty=true.
- const first_dbg_info_decl = self.dbg_info_decl_first orelse break :debug_info;
- const last_dbg_info_decl = self.dbg_info_decl_last.?;
- const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
-
- var di_buf = std.ArrayList(u8).init(self.base.allocator);
- defer di_buf.deinit();
-
- // We have a function to compute the upper bound size, because it's needed
- // for determining where to put the offset of the first `LinkBlock`.
- try di_buf.ensureCapacity(self.dbgInfoNeededHeaderBytes());
-
- // initial length - length of the .debug_info contribution for this compilation unit,
- // not including the initial length itself.
- // We have to come back and write it later after we know the size.
- const after_init_len = di_buf.items.len + init_len_size;
- // +1 for the final 0 that ends the compilation unit children.
- const dbg_info_end = last_dbg_info_decl.dbg_info_off + last_dbg_info_decl.dbg_info_len + 1;
- const init_len = dbg_info_end - after_init_len;
- switch (self.ptr_width) {
- .p32 => {
- mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian);
- },
- .p64 => {
- di_buf.appendNTimesAssumeCapacity(0xff, 4);
- mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian);
- },
- }
- mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // DWARF version
- const abbrev_offset = self.debug_abbrev_table_offset.?;
- switch (self.ptr_width) {
- .p32 => {
- mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, abbrev_offset), target_endian);
- di_buf.appendAssumeCapacity(4); // address size
- },
- .p64 => {
- mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), abbrev_offset, target_endian);
- di_buf.appendAssumeCapacity(8); // address size
- },
- }
- // 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 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.
- const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
- const low_pc = text_phdr.p_vaddr;
- const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
-
- di_buf.appendAssumeCapacity(abbrev_compile_unit);
- self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // DW.AT_stmt_list, DW.FORM_sec_offset
- self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc);
- self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc);
- self.writeDwarfAddrAssumeCapacity(&di_buf, name_strp);
- self.writeDwarfAddrAssumeCapacity(&di_buf, comp_dir_strp);
- self.writeDwarfAddrAssumeCapacity(&di_buf, producer_strp);
- // We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number:
- // http://dwarfstd.org/ShowIssue.php?issue=171115.1
- // Until then we say it is C99.
- mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG_C99, target_endian);
-
- if (di_buf.items.len > first_dbg_info_decl.dbg_info_off) {
- // Move the first N decls to the end to make more padding for the header.
- @panic("TODO: handle .debug_info header exceeding its padding");
- }
- const jmp_amt = first_dbg_info_decl.dbg_info_off - di_buf.items.len;
- try self.pwriteDbgInfoNops(0, di_buf.items, jmp_amt, false, debug_info_sect.sh_offset);
- self.debug_info_header_dirty = false;
- }
-
- if (self.debug_aranges_section_dirty) {
- const debug_aranges_sect = &self.sections.items[self.debug_aranges_section_index.?];
-
- var di_buf = std.ArrayList(u8).init(self.base.allocator);
- defer di_buf.deinit();
-
- // Enough for all the data without resizing. When support for more compilation units
- // is added, the size of this section will become more variable.
- try di_buf.ensureCapacity(100);
-
- // initial length - length of the .debug_aranges contribution for this compilation unit,
- // not including the initial length itself.
- // We have to come back and write it later after we know the size.
- const init_len_index = di_buf.items.len;
- di_buf.items.len += init_len_size;
- const after_init_len = di_buf.items.len;
- mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2, target_endian); // version
- // When more than one compilation unit is supported, this will be the offset to it.
- // For now it is always at offset 0 in .debug_info.
- self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // .debug_info offset
- di_buf.appendAssumeCapacity(ptr_width_bytes); // address_size
- di_buf.appendAssumeCapacity(0); // segment_selector_size
-
- const end_header_offset = di_buf.items.len;
- const begin_entries_offset = mem.alignForward(end_header_offset, ptr_width_bytes * 2);
- di_buf.appendNTimesAssumeCapacity(0, begin_entries_offset - end_header_offset);
-
- // Currently only one compilation unit is supported, so the address range is simply
- // identical to the main program header virtual address and memory size.
- const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
- self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_vaddr);
- self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_memsz);
-
- // Sentinel.
- self.writeDwarfAddrAssumeCapacity(&di_buf, 0);
- self.writeDwarfAddrAssumeCapacity(&di_buf, 0);
-
- // Go back and populate the initial length.
- const init_len = di_buf.items.len - after_init_len;
- switch (self.ptr_width) {
- .p32 => {
- mem.writeInt(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len), target_endian);
- },
- .p64 => {
- // initial length - length of the .debug_aranges contribution for this compilation unit,
- // not including the initial length itself.
- di_buf.items[init_len_index..][0..4].* = [_]u8{ 0xff, 0xff, 0xff, 0xff };
- mem.writeInt(u64, di_buf.items[init_len_index + 4 ..][0..8], init_len, target_endian);
- },
- }
-
- const needed_size = di_buf.items.len;
- const allocated_size = self.allocatedSize(debug_aranges_sect.sh_offset);
- if (needed_size > allocated_size) {
- debug_aranges_sect.sh_size = 0; // free the space
- debug_aranges_sect.sh_offset = self.findFreeSpace(needed_size, 16);
- }
- debug_aranges_sect.sh_size = needed_size;
- log.debug(".debug_aranges start=0x{x} end=0x{x}\n", .{
- debug_aranges_sect.sh_offset,
- debug_aranges_sect.sh_offset + needed_size,
- });
-
- try self.base.file.?.pwriteAll(di_buf.items, debug_aranges_sect.sh_offset);
- if (!self.shdr_table_dirty) {
- // Then it won't get written with the others and we need to do it.
- try self.writeSectHeader(self.debug_aranges_section_index.?);
- }
-
- self.debug_aranges_section_dirty = false;
- }
- if (self.debug_line_header_dirty) debug_line: {
- if (self.dbg_line_fn_first == null) {
- break :debug_line; // Error in module; leave debug_line_header_dirty=true.
- }
- const dbg_line_prg_off = self.getDebugLineProgramOff();
- const dbg_line_prg_end = self.getDebugLineProgramEnd();
- assert(dbg_line_prg_end != 0);
-
- const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
-
- var di_buf = std.ArrayList(u8).init(self.base.allocator);
- defer di_buf.deinit();
-
- // The size of this header is variable, depending on the number of directories,
- // files, and padding. We have a function to compute the upper bound size, however,
- // because it's needed for determining where to put the offset of the first `SrcFn`.
- try di_buf.ensureCapacity(self.dbgLineNeededHeaderBytes());
-
- // initial length - length of the .debug_line contribution for this compilation unit,
- // not including the initial length itself.
- const after_init_len = di_buf.items.len + init_len_size;
- const init_len = dbg_line_prg_end - after_init_len;
- switch (self.ptr_width) {
- .p32 => {
- mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian);
- },
- .p64 => {
- di_buf.appendNTimesAssumeCapacity(0xff, 4);
- mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian);
- },
- }
-
- mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // version
-
- // Empirically, debug info consumers do not respect this field, or otherwise
- // consider it to be an error when it does not point exactly to the end of the header.
- // Therefore we rely on the NOP jump at the beginning of the Line Number Program for
- // padding rather than this field.
- const before_header_len = di_buf.items.len;
- di_buf.items.len += ptr_width_bytes; // We will come back and write this.
- const after_header_len = di_buf.items.len;
-
- const opcode_base = DW.LNS_set_isa + 1;
- di_buf.appendSliceAssumeCapacity(&[_]u8{
- 1, // minimum_instruction_length
- 1, // maximum_operations_per_instruction
- 1, // default_is_stmt
- 1, // line_base (signed)
- 1, // line_range
- opcode_base,
-
- // Standard opcode lengths. The number of items here is based on `opcode_base`.
- // The value is the number of LEB128 operands the instruction takes.
- 0, // `DW.LNS_copy`
- 1, // `DW.LNS_advance_pc`
- 1, // `DW.LNS_advance_line`
- 1, // `DW.LNS_set_file`
- 1, // `DW.LNS_set_column`
- 0, // `DW.LNS_negate_stmt`
- 0, // `DW.LNS_set_basic_block`
- 0, // `DW.LNS_const_add_pc`
- 1, // `DW.LNS_fixed_advance_pc`
- 0, // `DW.LNS_set_prologue_end`
- 0, // `DW.LNS_set_epilogue_begin`
- 1, // `DW.LNS_set_isa`
-
- 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(&[_]u8{
- 0, // null byte for the relative path name
- 0, // directory_index
- 0, // mtime (TODO supply this)
- 0, // file size bytes (TODO supply this)
- 0, // file_names sentinel
- });
-
- const header_len = di_buf.items.len - after_header_len;
- switch (self.ptr_width) {
- .p32 => {
- mem.writeInt(u32, di_buf.items[before_header_len..][0..4], @intCast(u32, header_len), target_endian);
- },
- .p64 => {
- mem.writeInt(u64, di_buf.items[before_header_len..][0..8], header_len, target_endian);
- },
- }
-
- // We use NOPs because consumers empirically do not respect the header length field.
- if (di_buf.items.len > dbg_line_prg_off) {
- // Move the first N files to the end to make more padding for the header.
- @panic("TODO: handle .debug_line header exceeding its padding");
- }
- const jmp_amt = dbg_line_prg_off - di_buf.items.len;
- try self.pwriteDbgLineNops(0, di_buf.items, jmp_amt, debug_line_sect.sh_offset);
- self.debug_line_header_dirty = false;
- }
-
- if (self.phdr_table_dirty) {
- const phsize: u64 = switch (self.ptr_width) {
- .p32 => @sizeOf(elf.Elf32_Phdr),
- .p64 => @sizeOf(elf.Elf64_Phdr),
- };
- const phalign: u16 = switch (self.ptr_width) {
- .p32 => @alignOf(elf.Elf32_Phdr),
- .p64 => @alignOf(elf.Elf64_Phdr),
- };
- const allocated_size = self.allocatedSize(self.phdr_table_offset.?);
- const needed_size = self.program_headers.items.len * phsize;
-
- if (needed_size > allocated_size) {
- self.phdr_table_offset = null; // free the space
- self.phdr_table_offset = self.findFreeSpace(needed_size, phalign);
- }
-
- switch (self.ptr_width) {
- .p32 => {
- const buf = try self.base.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
- defer self.base.allocator.free(buf);
-
- for (buf) |*phdr, i| {
- phdr.* = progHeaderTo32(self.program_headers.items[i]);
- if (foreign_endian) {
- bswapAllFields(elf.Elf32_Phdr, phdr);
- }
- }
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
- },
- .p64 => {
- const buf = try self.base.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
- defer self.base.allocator.free(buf);
-
- for (buf) |*phdr, i| {
- phdr.* = self.program_headers.items[i];
- if (foreign_endian) {
- bswapAllFields(elf.Elf64_Phdr, phdr);
- }
- }
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
- },
- }
- self.phdr_table_dirty = false;
- }
-
- {
- const shstrtab_sect = &self.sections.items[self.shstrtab_index.?];
- if (self.shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {
- const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset);
- const needed_size = self.shstrtab.items.len;
-
- if (needed_size > allocated_size) {
- shstrtab_sect.sh_size = 0; // free the space
- shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
- }
- shstrtab_sect.sh_size = needed_size;
- log.debug("writing shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
-
- try self.base.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
- if (!self.shdr_table_dirty) {
- // Then it won't get written with the others and we need to do it.
- try self.writeSectHeader(self.shstrtab_index.?);
- }
- self.shstrtab_dirty = false;
- }
- }
- {
- const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?];
- if (self.debug_strtab_dirty or self.debug_strtab.items.len != debug_strtab_sect.sh_size) {
- const allocated_size = self.allocatedSize(debug_strtab_sect.sh_offset);
- const needed_size = self.debug_strtab.items.len;
-
- if (needed_size > allocated_size) {
- debug_strtab_sect.sh_size = 0; // free the space
- debug_strtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
- }
- debug_strtab_sect.sh_size = needed_size;
- log.debug("debug_strtab start=0x{x} end=0x{x}\n", .{ debug_strtab_sect.sh_offset, debug_strtab_sect.sh_offset + needed_size });
-
- try self.base.file.?.pwriteAll(self.debug_strtab.items, debug_strtab_sect.sh_offset);
- if (!self.shdr_table_dirty) {
- // Then it won't get written with the others and we need to do it.
- try self.writeSectHeader(self.debug_str_section_index.?);
- }
- self.debug_strtab_dirty = false;
- }
- }
- if (self.shdr_table_dirty) {
- const shsize: u64 = switch (self.ptr_width) {
- .p32 => @sizeOf(elf.Elf32_Shdr),
- .p64 => @sizeOf(elf.Elf64_Shdr),
- };
- const shalign: u16 = switch (self.ptr_width) {
- .p32 => @alignOf(elf.Elf32_Shdr),
- .p64 => @alignOf(elf.Elf64_Shdr),
- };
- const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
- const needed_size = self.sections.items.len * shsize;
-
- if (needed_size > allocated_size) {
- self.shdr_table_offset = null; // free the space
- self.shdr_table_offset = self.findFreeSpace(needed_size, shalign);
- }
-
- switch (self.ptr_width) {
- .p32 => {
- const buf = try self.base.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
- defer self.base.allocator.free(buf);
-
- for (buf) |*shdr, i| {
- shdr.* = sectHeaderTo32(self.sections.items[i]);
- log.debug("writing section {}\n", .{shdr.*});
- if (foreign_endian) {
- bswapAllFields(elf.Elf32_Shdr, shdr);
- }
- }
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
- },
- .p64 => {
- const buf = try self.base.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
- defer self.base.allocator.free(buf);
-
- for (buf) |*shdr, i| {
- shdr.* = self.sections.items[i];
- log.debug("writing section {}\n", .{shdr.*});
- if (foreign_endian) {
- bswapAllFields(elf.Elf64_Shdr, shdr);
- }
- }
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
- },
- }
- self.shdr_table_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;
- try self.writeElfHeader();
- }
-
- // The point of flush() is to commit changes, so in theory, nothing should
- // be dirty after this. However, it is possible for some things to remain
- // dirty because they fail to be written in the event of compile errors,
- // such as debug_line_header_dirty and debug_info_header_dirty.
- assert(!self.debug_abbrev_section_dirty);
- assert(!self.debug_aranges_section_dirty);
- assert(!self.phdr_table_dirty);
- assert(!self.shdr_table_dirty);
- assert(!self.shstrtab_dirty);
- assert(!self.debug_strtab_dirty);
-}
-
-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) {
- .p32 => mem.writeInt(u32, buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, addr), target_endian),
- .p64 => mem.writeInt(u64, buf.addManyAsArrayAssumeCapacity(8), addr, target_endian),
- }
-}
-
-fn writeElfHeader(self: *Elf) !void {
- var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
-
- var index: usize = 0;
- hdr_buf[0..4].* = "\x7fELF".*;
- index += 4;
-
- hdr_buf[index] = switch (self.ptr_width) {
- .p32 => elf.ELFCLASS32,
- .p64 => elf.ELFCLASS64,
- };
- index += 1;
-
- const endian = self.base.options.target.cpu.arch.endian();
- hdr_buf[index] = switch (endian) {
- .Little => elf.ELFDATA2LSB,
- .Big => elf.ELFDATA2MSB,
- };
- index += 1;
-
- hdr_buf[index] = 1; // ELF version
- index += 1;
-
- // OS ABI, often set to 0 regardless of target platform
- // ABI Version, possibly used by glibc but not by static executables
- // padding
- mem.set(u8, hdr_buf[index..][0..9], 0);
- index += 9;
-
- assert(index == 16);
-
- const elf_type = switch (self.base.options.output_mode) {
- .Exe => elf.ET.EXEC,
- .Obj => elf.ET.REL,
- .Lib => switch (self.base.options.link_mode) {
- .Static => elf.ET.REL,
- .Dynamic => elf.ET.DYN,
- },
- };
- mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
- index += 2;
-
- const machine = self.base.options.target.cpu.arch.toElfMachine();
- mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
- index += 2;
-
- // ELF Version, again
- mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
- index += 4;
-
- const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
-
- switch (self.ptr_width) {
- .p32 => {
- mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);
- index += 4;
-
- // e_phoff
- mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.phdr_table_offset.?), endian);
- index += 4;
-
- // e_shoff
- mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.shdr_table_offset.?), endian);
- index += 4;
- },
- .p64 => {
- // e_entry
- mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian);
- index += 8;
-
- // e_phoff
- mem.writeInt(u64, hdr_buf[index..][0..8], self.phdr_table_offset.?, endian);
- index += 8;
-
- // e_shoff
- mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian);
- index += 8;
- },
- }
-
- const e_flags = 0;
- mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);
- index += 4;
-
- const e_ehsize: u16 = switch (self.ptr_width) {
- .p32 => @sizeOf(elf.Elf32_Ehdr),
- .p64 => @sizeOf(elf.Elf64_Ehdr),
- };
- mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian);
- index += 2;
-
- const e_phentsize: u16 = switch (self.ptr_width) {
- .p32 => @sizeOf(elf.Elf32_Phdr),
- .p64 => @sizeOf(elf.Elf64_Phdr),
- };
- mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);
- index += 2;
-
- const e_phnum = @intCast(u16, self.program_headers.items.len);
- mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);
- index += 2;
-
- const e_shentsize: u16 = switch (self.ptr_width) {
- .p32 => @sizeOf(elf.Elf32_Shdr),
- .p64 => @sizeOf(elf.Elf64_Shdr),
- };
- mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);
- index += 2;
-
- const e_shnum = @intCast(u16, self.sections.items.len);
- mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
- index += 2;
-
- mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian);
- index += 2;
-
- assert(index == e_ehsize);
-
- try self.base.file.?.pwriteAll(hdr_buf[0..index], 0);
-}
-
-fn freeTextBlock(self: *Elf, 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;
- }
- }
- // TODO process free list for dbg info just like we do above for vaddrs
-
- if (self.last_text_block == text_block) {
- // TODO shrink the .text section size here
- self.last_text_block = text_block.prev;
- }
- if (self.dbg_info_decl_first == text_block) {
- self.dbg_info_decl_first = text_block.dbg_info_next;
- }
- if (self.dbg_info_decl_last == text_block) {
- // TODO shrink the .debug_info section size here
- self.dbg_info_decl_last = text_block.dbg_info_prev;
- }
-
- if (text_block.prev) |prev| {
- prev.next = text_block.next;
-
- if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
- // 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 {};
- }
- } else {
- text_block.prev = null;
- }
-
- if (text_block.next) |next| {
- next.prev = text_block.prev;
- } else {
- text_block.next = null;
- }
-
- if (text_block.dbg_info_prev) |prev| {
- prev.dbg_info_next = text_block.dbg_info_next;
-
- // TODO the free list logic like we do for text blocks above
- } else {
- text_block.dbg_info_prev = null;
- }
-
- if (text_block.dbg_info_next) |next| {
- next.dbg_info_prev = text_block.dbg_info_prev;
- } else {
- text_block.dbg_info_next = null;
- }
-}
-
-fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64) void {
- // TODO check the new capacity, and if it crosses the size threshold into a big enough
- // capacity, insert a free list node for it.
-}
-
-fn growTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
- const sym = self.local_symbols.items[text_block.local_sym_index];
- const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;
- const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
- if (!need_realloc) return sym.st_value;
- return self.allocateTextBlock(text_block, new_block_size, alignment);
-}
-
-fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
- const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
- const shdr = &self.sections.items[self.text_section_index.?];
- const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
-
- // 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;
-
- // First we look for an appropriately sized free list node.
- // The list is unordered. We'll just take the first thing that works.
- const vaddr = blk: {
- var i: usize = 0;
- while (i < self.text_block_free_list.items.len) {
- const big_block = self.text_block_free_list.items[i];
- // We now have a pointer to a live text block that has too much capacity.
- // Is it enough that we could fit this new text block?
- const sym = self.local_symbols.items[big_block.local_sym_index];
- const capacity = big_block.capacity(self.*);
- const ideal_capacity = capacity * alloc_num / alloc_den;
- const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
- const capacity_end_vaddr = sym.st_value + capacity;
- const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;
- const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
- if (new_start_vaddr < ideal_capacity_end_vaddr) {
- // Additional bookkeeping here to notice if this free list node
- // should be deleted because the block that it points to has grown to take up
- // more of the extra capacity.
- if (!big_block.freeListEligible(self.*)) {
- _ = self.text_block_free_list.swapRemove(i);
- } else {
- i += 1;
- }
- continue;
- }
- // At this point we know that we will place the new block here. But the
- // remaining question is whether there is still yet enough capacity left
- // over for there to still be a free list node.
- const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
- const keep_free_list_node = remaining_capacity >= min_text_capacity;
-
- // Set up the metadata to be updated, after errors are no longer possible.
- block_placement = big_block;
- if (!keep_free_list_node) {
- free_list_removal = i;
- }
- break :blk new_start_vaddr;
- } else if (self.last_text_block) |last| {
- const sym = self.local_symbols.items[last.local_sym_index];
- const ideal_capacity = sym.st_size * alloc_num / alloc_den;
- const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
- const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
- // Set up the metadata to be updated, after errors are no longer possible.
- block_placement = last;
- break :blk new_start_vaddr;
- } else {
- break :blk phdr.p_vaddr;
- }
- };
-
- const expand_text_section = block_placement == null or block_placement.?.next == null;
- if (expand_text_section) {
- const text_capacity = self.allocatedSize(shdr.sh_offset);
- const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;
- if (needed_size > text_capacity) {
- // Must move the entire text section.
- const new_offset = self.findFreeSpace(needed_size, 0x1000);
- const text_size = if (self.last_text_block) |last| blk: {
- const sym = self.local_symbols.items[last.local_sym_index];
- break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
- } else 0;
- const amt = try self.base.file.?.copyRangeAll(shdr.sh_offset, self.base.file.?, new_offset, text_size);
- if (amt != text_size) return error.InputOutput;
- shdr.sh_offset = new_offset;
- phdr.p_offset = new_offset;
- }
- self.last_text_block = text_block;
-
- shdr.sh_size = needed_size;
- phdr.p_memsz = needed_size;
- phdr.p_filesz = needed_size;
-
- // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address
- // range of the compilation unit. When we expand the text section, this range changes,
- // so the DW_TAG_compile_unit tag of the .debug_info section becomes dirty.
- self.debug_info_header_dirty = true;
- // This becomes dirty for the same reason. We could potentially make this more
- // fine-grained with the addition of support for more compilation units. It is planned to
- // model each package as a different compilation unit.
- self.debug_aranges_section_dirty = true;
-
- self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
- self.shdr_table_dirty = true; // TODO look into making only the one section dirty
- }
-
- // 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;
-}
-
-pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
- if (decl.link.elf.local_sym_index != 0) return;
-
- try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1);
- try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
-
- if (self.local_symbol_free_list.popOrNull()) |i| {
- log.debug("reusing symbol index {} for {}\n", .{ i, decl.name });
- decl.link.elf.local_sym_index = i;
- } else {
- log.debug("allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });
- decl.link.elf.local_sym_index = @intCast(u32, self.local_symbols.items.len);
- _ = self.local_symbols.addOneAssumeCapacity();
- }
-
- if (self.offset_table_free_list.popOrNull()) |i| {
- decl.link.elf.offset_table_index = i;
- } else {
- decl.link.elf.offset_table_index = @intCast(u32, self.offset_table.items.len);
- _ = self.offset_table.addOneAssumeCapacity();
- self.offset_table_count_dirty = true;
- }
-
- const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
-
- self.local_symbols.items[decl.link.elf.local_sym_index] = .{
- .st_name = 0,
- .st_info = 0,
- .st_other = 0,
- .st_shndx = 0,
- .st_value = phdr.p_vaddr,
- .st_size = 0,
- };
- self.offset_table.items[decl.link.elf.offset_table_index] = 0;
-}
-
-pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
- // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
- self.freeTextBlock(&decl.link.elf);
- if (decl.link.elf.local_sym_index != 0) {
- self.local_symbol_free_list.append(self.base.allocator, decl.link.elf.local_sym_index) catch {};
- self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {};
-
- self.local_symbols.items[decl.link.elf.local_sym_index].st_info = 0;
-
- decl.link.elf.local_sym_index = 0;
- }
- // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing
- // is desired for both.
- _ = self.dbg_line_fn_free_list.remove(&decl.fn_link.elf);
- if (decl.fn_link.elf.prev) |prev| {
- _ = self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
- prev.next = decl.fn_link.elf.next;
- if (decl.fn_link.elf.next) |next| {
- next.prev = prev;
- } else {
- self.dbg_line_fn_last = prev;
- }
- } else if (decl.fn_link.elf.next) |next| {
- self.dbg_line_fn_first = next;
- next.prev = null;
- }
- if (self.dbg_line_fn_first == &decl.fn_link.elf) {
- self.dbg_line_fn_first = decl.fn_link.elf.next;
- }
- if (self.dbg_line_fn_last == &decl.fn_link.elf) {
- self.dbg_line_fn_last = decl.fn_link.elf.prev;
- }
-}
-
-pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- var code_buffer = std.ArrayList(u8).init(self.base.allocator);
- defer code_buffer.deinit();
-
- var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator);
- defer dbg_line_buffer.deinit();
-
- var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator);
- defer dbg_info_buffer.deinit();
-
- var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{};
- defer {
- var it = dbg_info_type_relocs.iterator();
- while (it.next()) |entry| {
- entry.value.relocs.deinit(self.base.allocator);
- }
- dbg_info_type_relocs.deinit(self.base.allocator);
- }
-
- const typed_value = decl.typed_value.most_recent.typed_value;
- const is_fn: bool = switch (typed_value.ty.zigTypeTag()) {
- .Fn => true,
- else => false,
- };
- if (is_fn) {
- const zir_dumps = if (std.builtin.is_test) &[0][]const u8{} else build_options.zir_dumps;
- if (zir_dumps.len != 0) {
- for (zir_dumps) |fn_name| {
- if (mem.eql(u8, mem.spanZ(decl.name), fn_name)) {
- std.debug.print("\n{}\n", .{decl.name});
- typed_value.val.cast(Value.Payload.Function).?.func.dump(module.*);
- }
- }
- }
-
- // For functions we need to add a prologue to the debug line program.
- try dbg_line_buffer.ensureCapacity(26);
-
- const line_off: u28 = blk: {
- if (decl.scope.cast(Module.Scope.Container)) |container_scope| {
- const tree = container_scope.file_scope.contents.tree;
- const file_ast_decls = tree.root_node.decls();
- // TODO Look into improving the performance here by adding a token-index-to-line
- // lookup table. Currently this involves scanning over the source code for newlines.
- const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
- const block = fn_proto.getBodyNode().?.castTag(.Block).?;
- const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
- break :blk @intCast(u28, line_delta);
- } else if (decl.scope.cast(Module.Scope.ZIRModule)) |zir_module| {
- const byte_off = zir_module.contents.module.decls[decl.src_index].inst.src;
- const line_delta = std.zig.lineDelta(zir_module.source.bytes, 0, byte_off);
- break :blk @intCast(u28, line_delta);
- } else {
- unreachable;
- }
- };
-
- const ptr_width_bytes = self.ptrWidthBytes();
- dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
- DW.LNS_extended_op,
- ptr_width_bytes + 1,
- DW.LNE_set_address,
- });
- // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`.
- assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len);
- dbg_line_buffer.items.len += ptr_width_bytes;
-
- dbg_line_buffer.appendAssumeCapacity(DW.LNS_advance_line);
- // This is the "relocatable" relative line offset from the previous function's end curly
- // to this function's begin curly.
- assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len);
- // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
- leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off);
-
- dbg_line_buffer.appendAssumeCapacity(DW.LNS_set_file);
- assert(self.getRelocDbgFileIndex() == dbg_line_buffer.items.len);
- // Once we support more than one source file, this will have the ability to be more
- // than one possible value.
- const file_index = 1;
- leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index);
-
- // Emit a line for the begin curly with prologue_end=false. The codegen will
- // do the work of setting prologue_end=true and epilogue_begin=true.
- dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy);
-
- // .debug_info subprogram
- const decl_name_with_null = decl.name[0 .. mem.lenZ(decl.name) + 1];
- try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 25 + decl_name_with_null.len);
-
- const fn_ret_type = typed_value.ty.fnReturnType();
- const fn_ret_has_bits = fn_ret_type.hasCodeGenBits();
- if (fn_ret_has_bits) {
- dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram);
- } else {
- dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid);
- }
- // These get overwritten after generating the machine code. These values are
- // "relocations" and have to be in this fixed place so that functions can be
- // moved in virtual address space.
- assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len);
- dbg_info_buffer.items.len += ptr_width_bytes; // DW.AT_low_pc, DW.FORM_addr
- assert(self.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len);
- dbg_info_buffer.items.len += 4; // DW.AT_high_pc, DW.FORM_data4
- if (fn_ret_has_bits) {
- const gop = try dbg_info_type_relocs.getOrPut(self.base.allocator, fn_ret_type);
- if (!gop.found_existing) {
- gop.entry.value = .{
- .off = undefined,
- .relocs = .{},
- };
- }
- try gop.entry.value.relocs.append(self.base.allocator, @intCast(u32, dbg_info_buffer.items.len));
- dbg_info_buffer.items.len += 4; // DW.AT_type, DW.FORM_ref4
- }
- dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT_name, DW.FORM_string
- } else {
- // TODO implement .debug_info for global variables
- }
- const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .{
- .dwarf = .{
- .dbg_line = &dbg_line_buffer,
- .dbg_info = &dbg_info_buffer,
- .dbg_info_type_relocs = &dbg_info_type_relocs,
- },
- });
- 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 stt_bits: u8 = if (is_fn) elf.STT_FUNC else elf.STT_OBJECT;
-
- assert(decl.link.elf.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
- const local_sym = &self.local_symbols.items[decl.link.elf.local_sym_index];
- if (local_sym.st_size != 0) {
- const capacity = decl.link.elf.capacity(self.*);
- const need_realloc = code.len > capacity or
- !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
- if (need_realloc) {
- const vaddr = try self.growTextBlock(&decl.link.elf, code.len, required_alignment);
- log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
- if (vaddr != local_sym.st_value) {
- local_sym.st_value = vaddr;
-
- log.debug(" (writing new offset table entry)\n", .{});
- self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;
- try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
- }
- } else if (code.len < local_sym.st_size) {
- self.shrinkTextBlock(&decl.link.elf, code.len);
- }
- local_sym.st_size = code.len;
- local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name));
- local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
- local_sym.st_other = 0;
- local_sym.st_shndx = self.text_section_index.?;
- // TODO this write could be avoided if no fields of the symbol were changed.
- try self.writeSymbol(decl.link.elf.local_sym_index);
- } else {
- const decl_name = mem.spanZ(decl.name);
- const name_str_index = try self.makeString(decl_name);
- const vaddr = try self.allocateTextBlock(&decl.link.elf, code.len, required_alignment);
- log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
- errdefer self.freeTextBlock(&decl.link.elf);
-
- local_sym.* = .{
- .st_name = name_str_index,
- .st_info = (elf.STB_LOCAL << 4) | stt_bits,
- .st_other = 0,
- .st_shndx = self.text_section_index.?,
- .st_value = vaddr,
- .st_size = code.len,
- };
- self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;
-
- try self.writeSymbol(decl.link.elf.local_sym_index);
- try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
- }
-
- const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;
- const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
- try self.base.file.?.pwriteAll(code, file_offset);
-
- const target_endian = self.base.options.target.cpu.arch.endian();
-
- const text_block = &decl.link.elf;
-
- // If the Decl is a function, we need to update the .debug_line program.
- if (is_fn) {
- // Perform the relocations based on vaddr.
- switch (self.ptr_width) {
- .p32 => {
- {
- const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4];
- mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);
- }
- {
- const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..4];
- mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);
- }
- },
- .p64 => {
- {
- const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8];
- mem.writeInt(u64, ptr, local_sym.st_value, target_endian);
- }
- {
- const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..8];
- mem.writeInt(u64, ptr, local_sym.st_value, target_endian);
- }
- },
- }
- {
- const ptr = dbg_info_buffer.items[self.getRelocDbgInfoSubprogramHighPC()..][0..4];
- mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_size), target_endian);
- }
-
- try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence });
-
- // Now we have the full contents and may allocate a region to store it.
-
- // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for
- // `TextBlock` and the .debug_info. If you are editing this logic, you
- // probably need to edit that logic too.
-
- const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
- const src_fn = &decl.fn_link.elf;
- src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
- if (self.dbg_line_fn_last) |last| {
- if (src_fn.next) |next| {
- // Update existing function - non-last item.
- if (src_fn.off + src_fn.len + min_nop_size > next.off) {
- // It grew too big, so we move it to a new location.
- if (src_fn.prev) |prev| {
- _ = self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
- prev.next = src_fn.next;
- }
- next.prev = src_fn.prev;
- src_fn.next = null;
- // Populate where it used to be with NOPs.
- const file_pos = debug_line_sect.sh_offset + src_fn.off;
- try self.pwriteDbgLineNops(0, &[0]u8{}, src_fn.len, file_pos);
- // TODO Look at the free list before appending at the end.
- src_fn.prev = last;
- last.next = src_fn;
- self.dbg_line_fn_last = src_fn;
-
- src_fn.off = last.off + (last.len * alloc_num / alloc_den);
- }
- } else if (src_fn.prev == null) {
- // Append new function.
- // TODO Look at the free list before appending at the end.
- src_fn.prev = last;
- last.next = src_fn;
- self.dbg_line_fn_last = src_fn;
-
- src_fn.off = last.off + (last.len * alloc_num / alloc_den);
- }
- } else {
- // This is the first function of the Line Number Program.
- self.dbg_line_fn_first = src_fn;
- self.dbg_line_fn_last = src_fn;
-
- src_fn.off = self.dbgLineNeededHeaderBytes() * alloc_num / alloc_den;
- }
-
- const last_src_fn = self.dbg_line_fn_last.?;
- const needed_size = last_src_fn.off + last_src_fn.len;
- if (needed_size != debug_line_sect.sh_size) {
- if (needed_size > self.allocatedSize(debug_line_sect.sh_offset)) {
- const new_offset = self.findFreeSpace(needed_size, 1);
- const existing_size = last_src_fn.off;
- log.debug("moving .debug_line section: {} bytes from 0x{x} to 0x{x}\n", .{
- existing_size,
- debug_line_sect.sh_offset,
- new_offset,
- });
- const amt = try self.base.file.?.copyRangeAll(debug_line_sect.sh_offset, self.base.file.?, new_offset, existing_size);
- if (amt != existing_size) return error.InputOutput;
- debug_line_sect.sh_offset = new_offset;
- }
- debug_line_sect.sh_size = needed_size;
- self.shdr_table_dirty = true; // TODO look into making only the one section dirty
- self.debug_line_header_dirty = true;
- }
- const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0;
- const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0;
-
- // We only have support for one compilation unit so far, so the offsets are directly
- // from the .debug_line section.
- const file_pos = debug_line_sect.sh_offset + src_fn.off;
- try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos);
-
- // .debug_info - End the TAG_subprogram children.
- try dbg_info_buffer.append(0);
- }
-
- // Now we emit the .debug_info types of the Decl. These will count towards the size of
- // the buffer, so we have to do it before computing the offset, and we can't perform the actual
- // relocations yet.
- var it = dbg_info_type_relocs.iterator();
- while (it.next()) |entry| {
- entry.value.off = @intCast(u32, dbg_info_buffer.items.len);
- try self.addDbgInfoType(entry.key, &dbg_info_buffer);
- }
-
- try self.updateDeclDebugInfoAllocation(text_block, @intCast(u32, dbg_info_buffer.items.len));
-
- // Now that we have the offset assigned we can finally perform type relocations.
- it = dbg_info_type_relocs.iterator();
- while (it.next()) |entry| {
- for (entry.value.relocs.items) |off| {
- mem.writeInt(
- u32,
- dbg_info_buffer.items[off..][0..4],
- text_block.dbg_info_off + entry.value.off,
- target_endian,
- );
- }
- }
-
- try self.writeDeclDebugInfo(text_block, dbg_info_buffer.items);
-
- // 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);
-}
-
-/// Asserts the type has codegen bits.
-fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !void {
- switch (ty.zigTypeTag()) {
- .Void => unreachable,
- .NoReturn => unreachable,
- .Bool => {
- try dbg_info_buffer.appendSlice(&[_]u8{
- abbrev_base_type,
- DW.ATE_boolean, // DW.AT_encoding , DW.FORM_data1
- 1, // DW.AT_byte_size, DW.FORM_data1
- 'b',
- 'o',
- 'o',
- 'l',
- 0, // DW.AT_name, DW.FORM_string
- });
- },
- .Int => {
- const info = ty.intInfo(self.base.options.target);
- try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12);
- dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);
- // DW.AT_encoding, DW.FORM_data1
- dbg_info_buffer.appendAssumeCapacity(if (info.signed) DW.ATE_signed else DW.ATE_unsigned);
- // DW.AT_byte_size, DW.FORM_data1
- dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(self.base.options.target)));
- // DW.AT_name, DW.FORM_string
- try dbg_info_buffer.writer().print("{}\x00", .{ty});
- },
- else => {
- std.log.scoped(.compiler).err("TODO implement .debug_info for type '{}'", .{ty});
- try dbg_info_buffer.append(abbrev_pad1);
- },
- }
-}
-
-fn updateDeclDebugInfoAllocation(self: *Elf, text_block: *TextBlock, len: u32) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- // This logic is nearly identical to the logic above in `updateDecl` for
- // `SrcFn` and the line number programs. If you are editing this logic, you
- // probably need to edit that logic too.
-
- const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
- text_block.dbg_info_len = len;
- if (self.dbg_info_decl_last) |last| {
- if (text_block.dbg_info_next) |next| {
- // Update existing Decl - non-last item.
- if (text_block.dbg_info_off + text_block.dbg_info_len + min_nop_size > next.dbg_info_off) {
- // It grew too big, so we move it to a new location.
- if (text_block.dbg_info_prev) |prev| {
- _ = self.dbg_info_decl_free_list.put(self.base.allocator, prev, {}) catch {};
- prev.dbg_info_next = text_block.dbg_info_next;
- }
- next.dbg_info_prev = text_block.dbg_info_prev;
- text_block.dbg_info_next = null;
- // Populate where it used to be with NOPs.
- const file_pos = debug_info_sect.sh_offset + text_block.dbg_info_off;
- try self.pwriteDbgInfoNops(0, &[0]u8{}, text_block.dbg_info_len, false, file_pos);
- // TODO Look at the free list before appending at the end.
- text_block.dbg_info_prev = last;
- last.dbg_info_next = text_block;
- self.dbg_info_decl_last = text_block;
-
- text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den);
- }
- } else if (text_block.dbg_info_prev == null) {
- // Append new Decl.
- // TODO Look at the free list before appending at the end.
- text_block.dbg_info_prev = last;
- last.dbg_info_next = text_block;
- self.dbg_info_decl_last = text_block;
-
- text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den);
- }
- } else {
- // This is the first Decl of the .debug_info
- self.dbg_info_decl_first = text_block;
- self.dbg_info_decl_last = text_block;
-
- text_block.dbg_info_off = self.dbgInfoNeededHeaderBytes() * alloc_num / alloc_den;
- }
-}
-
-fn writeDeclDebugInfo(self: *Elf, text_block: *TextBlock, dbg_info_buf: []const u8) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- // This logic is nearly identical to the logic above in `updateDecl` for
- // `SrcFn` and the line number programs. If you are editing this logic, you
- // probably need to edit that logic too.
-
- const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
-
- const last_decl = self.dbg_info_decl_last.?;
- // +1 for a trailing zero to end the children of the decl tag.
- const needed_size = last_decl.dbg_info_off + last_decl.dbg_info_len + 1;
- if (needed_size != debug_info_sect.sh_size) {
- if (needed_size > self.allocatedSize(debug_info_sect.sh_offset)) {
- const new_offset = self.findFreeSpace(needed_size, 1);
- const existing_size = last_decl.dbg_info_off;
- log.debug("moving .debug_info section: {} bytes from 0x{x} to 0x{x}\n", .{
- existing_size,
- debug_info_sect.sh_offset,
- new_offset,
- });
- const amt = try self.base.file.?.copyRangeAll(debug_info_sect.sh_offset, self.base.file.?, new_offset, existing_size);
- if (amt != existing_size) return error.InputOutput;
- debug_info_sect.sh_offset = new_offset;
- }
- debug_info_sect.sh_size = needed_size;
- self.shdr_table_dirty = true; // TODO look into making only the one section dirty
- self.debug_info_header_dirty = true;
- }
- const prev_padding_size: u32 = if (text_block.dbg_info_prev) |prev|
- text_block.dbg_info_off - (prev.dbg_info_off + prev.dbg_info_len)
- else
- 0;
- const next_padding_size: u32 = if (text_block.dbg_info_next) |next|
- next.dbg_info_off - (text_block.dbg_info_off + text_block.dbg_info_len)
- else
- 0;
-
- // To end the children of the decl tag.
- const trailing_zero = text_block.dbg_info_next == null;
-
- // We only have support for one compilation unit so far, so the offsets are directly
- // from the .debug_info section.
- const file_pos = debug_info_sect.sh_offset + text_block.dbg_info_off;
- try self.pwriteDbgInfoNops(prev_padding_size, dbg_info_buf, next_padding_size, trailing_zero, file_pos);
-}
-
-pub fn updateDeclExports(
- self: *Elf,
- module: *Module,
- decl: *const Module.Decl,
- exports: []const *Module.Export,
-) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- try self.global_symbols.ensureCapacity(self.base.allocator, self.global_symbols.items.len + exports.len);
- const typed_value = decl.typed_value.most_recent.typed_value;
- if (decl.link.elf.local_sym_index == 0) return;
- const decl_sym = self.local_symbols.items[decl.link.elf.local_sym_index];
-
- 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 Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
- );
- continue;
- }
- }
- const stb_bits: u8 = switch (exp.options.linkage) {
- .Internal => elf.STB_LOCAL,
- .Strong => blk: {
- if (mem.eql(u8, exp.options.name, "_start")) {
- self.entry_addr = decl_sym.st_value;
- }
- break :blk elf.STB_GLOBAL;
- },
- .Weak => elf.STB_WEAK,
- .LinkOnce => {
- 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", .{}),
- );
- continue;
- },
- };
- const stt_bits: u8 = @truncate(u4, decl_sym.st_info);
- if (exp.link.sym_index) |i| {
- const sym = &self.global_symbols.items[i];
- sym.* = .{
- .st_name = try self.updateString(sym.st_name, exp.options.name),
- .st_info = (stb_bits << 4) | stt_bits,
- .st_other = 0,
- .st_shndx = self.text_section_index.?,
- .st_value = decl_sym.st_value,
- .st_size = decl_sym.st_size,
- };
- } else {
- const name = try self.makeString(exp.options.name);
- const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
- _ = self.global_symbols.addOneAssumeCapacity();
- break :blk self.global_symbols.items.len - 1;
- };
- self.global_symbols.items[i] = .{
- .st_name = name,
- .st_info = (stb_bits << 4) | stt_bits,
- .st_other = 0,
- .st_shndx = self.text_section_index.?,
- .st_value = decl_sym.st_value,
- .st_size = decl_sym.st_size,
- };
-
- exp.link.sym_index = @intCast(u32, i);
- }
- }
-}
-
-/// Must be called only after a successful call to `updateDecl`.
-pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Decl) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const container_scope = decl.scope.cast(Module.Scope.Container).?;
- const tree = container_scope.file_scope.contents.tree;
- const file_ast_decls = tree.root_node.decls();
- // TODO Look into improving the performance here by adding a token-index-to-line
- // lookup table. Currently this involves scanning over the source code for newlines.
- const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
- const block = fn_proto.getBodyNode().?.castTag(.Block).?;
- const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
- const casted_line_off = @intCast(u28, line_delta);
-
- const shdr = &self.sections.items[self.debug_line_section_index.?];
- const file_pos = shdr.sh_offset + decl.fn_link.elf.off + self.getRelocDbgLineOff();
- var data: [4]u8 = undefined;
- leb128.writeUnsignedFixed(4, &data, casted_line_off);
- try self.base.file.?.pwriteAll(&data, file_pos);
-}
-
-pub fn deleteExport(self: *Elf, exp: Export) void {
- const sym_index = exp.sym_index orelse return;
- self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {};
- self.global_symbols.items[sym_index].st_info = 0;
-}
-
-fn writeProgHeader(self: *Elf, index: usize) !void {
- const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
- const offset = self.program_headers.items[index].p_offset;
- switch (self.ptr_width) {
- .p32 => {
- var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
- if (foreign_endian) {
- bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
- }
- return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
- },
- .p64 => {
- var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
- if (foreign_endian) {
- bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
- }
- return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
- },
- }
-}
-
-fn writeSectHeader(self: *Elf, index: usize) !void {
- const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
- switch (self.ptr_width) {
- .p32 => {
- var shdr: [1]elf.Elf32_Shdr = undefined;
- shdr[0] = sectHeaderTo32(self.sections.items[index]);
- if (foreign_endian) {
- bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
- }
- const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr);
- return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
- },
- .p64 => {
- var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
- if (foreign_endian) {
- bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
- }
- const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr);
- return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
- },
- }
-}
-
-fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
- const shdr = &self.sections.items[self.got_section_index.?];
- const phdr = &self.program_headers.items[self.phdr_got_index.?];
- const entry_size: u16 = self.archPtrWidthBytes();
- if (self.offset_table_count_dirty) {
- // TODO Also detect virtual address collisions.
- const allocated_size = self.allocatedSize(shdr.sh_offset);
- const needed_size = self.local_symbols.items.len * entry_size;
- if (needed_size > allocated_size) {
- // Must move the entire got section.
- const new_offset = self.findFreeSpace(needed_size, entry_size);
- const amt = try self.base.file.?.copyRangeAll(shdr.sh_offset, self.base.file.?, new_offset, shdr.sh_size);
- if (amt != shdr.sh_size) return error.InputOutput;
- shdr.sh_offset = new_offset;
- phdr.p_offset = new_offset;
- }
- shdr.sh_size = needed_size;
- phdr.p_memsz = needed_size;
- phdr.p_filesz = needed_size;
-
- self.shdr_table_dirty = true; // TODO look into making only the one section dirty
- self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
-
- self.offset_table_count_dirty = false;
- }
- const endian = self.base.options.target.cpu.arch.endian();
- const off = shdr.sh_offset + @as(u64, entry_size) * index;
- switch (entry_size) {
- 2 => {
- var buf: [2]u8 = undefined;
- mem.writeInt(u16, &buf, @intCast(u16, self.offset_table.items[index]), endian);
- try self.base.file.?.pwriteAll(&buf, off);
- },
- 4 => {
- var buf: [4]u8 = undefined;
- mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
- try self.base.file.?.pwriteAll(&buf, off);
- },
- 8 => {
- var buf: [8]u8 = undefined;
- mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
- try self.base.file.?.pwriteAll(&buf, off);
- },
- else => unreachable,
- }
-}
-
-fn writeSymbol(self: *Elf, index: usize) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const syms_sect = &self.sections.items[self.symtab_section_index.?];
- // Make sure we are not pointlessly writing symbol data that will have to get relocated
- // due to running out of space.
- if (self.local_symbols.items.len != syms_sect.sh_info) {
- const sym_size: u64 = switch (self.ptr_width) {
- .p32 => @sizeOf(elf.Elf32_Sym),
- .p64 => @sizeOf(elf.Elf64_Sym),
- };
- const sym_align: u16 = switch (self.ptr_width) {
- .p32 => @alignOf(elf.Elf32_Sym),
- .p64 => @alignOf(elf.Elf64_Sym),
- };
- const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size;
- if (needed_size > self.allocatedSize(syms_sect.sh_offset)) {
- // Move all the symbols to a new file location.
- const new_offset = self.findFreeSpace(needed_size, sym_align);
- const existing_size = @as(u64, syms_sect.sh_info) * sym_size;
- const amt = try self.base.file.?.copyRangeAll(syms_sect.sh_offset, self.base.file.?, new_offset, existing_size);
- if (amt != existing_size) return error.InputOutput;
- syms_sect.sh_offset = new_offset;
- }
- syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len);
- syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
- self.shdr_table_dirty = true; // TODO look into only writing one section
- }
- const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
- switch (self.ptr_width) {
- .p32 => {
- var sym = [1]elf.Elf32_Sym{
- .{
- .st_name = self.local_symbols.items[index].st_name,
- .st_value = @intCast(u32, self.local_symbols.items[index].st_value),
- .st_size = @intCast(u32, self.local_symbols.items[index].st_size),
- .st_info = self.local_symbols.items[index].st_info,
- .st_other = self.local_symbols.items[index].st_other,
- .st_shndx = self.local_symbols.items[index].st_shndx,
- },
- };
- if (foreign_endian) {
- bswapAllFields(elf.Elf32_Sym, &sym[0]);
- }
- const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
- },
- .p64 => {
- var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};
- if (foreign_endian) {
- bswapAllFields(elf.Elf64_Sym, &sym[0]);
- }
- const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
- },
- }
-}
-
-fn writeAllGlobalSymbols(self: *Elf) !void {
- const syms_sect = &self.sections.items[self.symtab_section_index.?];
- const sym_size: u64 = switch (self.ptr_width) {
- .p32 => @sizeOf(elf.Elf32_Sym),
- .p64 => @sizeOf(elf.Elf64_Sym),
- };
- const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
- const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;
- switch (self.ptr_width) {
- .p32 => {
- const buf = try self.base.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len);
- defer self.base.allocator.free(buf);
-
- for (buf) |*sym, i| {
- sym.* = .{
- .st_name = self.global_symbols.items[i].st_name,
- .st_value = @intCast(u32, self.global_symbols.items[i].st_value),
- .st_size = @intCast(u32, self.global_symbols.items[i].st_size),
- .st_info = self.global_symbols.items[i].st_info,
- .st_other = self.global_symbols.items[i].st_other,
- .st_shndx = self.global_symbols.items[i].st_shndx,
- };
- if (foreign_endian) {
- bswapAllFields(elf.Elf32_Sym, sym);
- }
- }
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
- },
- .p64 => {
- const buf = try self.base.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);
- defer self.base.allocator.free(buf);
-
- for (buf) |*sym, i| {
- sym.* = .{
- .st_name = self.global_symbols.items[i].st_name,
- .st_value = self.global_symbols.items[i].st_value,
- .st_size = self.global_symbols.items[i].st_size,
- .st_info = self.global_symbols.items[i].st_info,
- .st_other = self.global_symbols.items[i].st_other,
- .st_shndx = self.global_symbols.items[i].st_shndx,
- };
- if (foreign_endian) {
- bswapAllFields(elf.Elf64_Sym, sym);
- }
- }
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
- },
- }
-}
-
-/// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF.
-fn ptrWidthBytes(self: Elf) u8 {
- return switch (self.ptr_width) {
- .p32 => 4,
- .p64 => 8,
- };
-}
-
-/// Does not necessarily match `ptrWidthBytes` for example can be 2 bytes
-/// in a 32-bit ELF file.
-fn archPtrWidthBytes(self: Elf) u8 {
- return @intCast(u8, self.base.options.target.cpu.arch.ptrBitWidth() / 8);
-}
-
-/// The reloc offset for the virtual address of a function in its Line Number Program.
-/// Size is a virtual address integer.
-const dbg_line_vaddr_reloc_index = 3;
-/// The reloc offset for the virtual address of a function in its .debug_info TAG_subprogram.
-/// Size is a virtual address integer.
-const dbg_info_low_pc_reloc_index = 1;
-
-/// The reloc offset for the line offset of a function from the previous function's line.
-/// It's a fixed-size 4-byte ULEB128.
-fn getRelocDbgLineOff(self: Elf) usize {
- return dbg_line_vaddr_reloc_index + self.ptrWidthBytes() + 1;
-}
-
-fn getRelocDbgFileIndex(self: Elf) usize {
- return self.getRelocDbgLineOff() + 5;
-}
-
-fn getRelocDbgInfoSubprogramHighPC(self: Elf) u32 {
- return dbg_info_low_pc_reloc_index + self.ptrWidthBytes();
-}
-
-fn dbgLineNeededHeaderBytes(self: Elf) u32 {
- const directory_entry_format_count = 1;
- const file_name_entry_format_count = 1;
- const directory_count = 1;
- const file_name_count = 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);
-}
-
-fn dbgInfoNeededHeaderBytes(self: Elf) u32 {
- return 120;
-}
-
-const min_nop_size = 2;
-
-/// Writes to the file a buffer, prefixed and suffixed by the specified number of
-/// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes
-/// are less than 126,976 bytes (if this limit is ever reached, this function can be
-/// improved to make more than one pwritev call, or the limit can be raised by a fixed
-/// amount by increasing the length of `vecs`).
-fn pwriteDbgLineNops(
- self: *Elf,
- prev_padding_size: usize,
- buf: []const u8,
- next_padding_size: usize,
- offset: usize,
-) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096;
- const three_byte_nop = [3]u8{ DW.LNS_advance_pc, 0b1000_0000, 0 };
- var vecs: [32]std.os.iovec_const = undefined;
- var vec_index: usize = 0;
- {
- var padding_left = prev_padding_size;
- if (padding_left % 2 != 0) {
- vecs[vec_index] = .{
- .iov_base = &three_byte_nop,
- .iov_len = three_byte_nop.len,
- };
- vec_index += 1;
- padding_left -= three_byte_nop.len;
- }
- while (padding_left > page_of_nops.len) {
- vecs[vec_index] = .{
- .iov_base = &page_of_nops,
- .iov_len = page_of_nops.len,
- };
- vec_index += 1;
- padding_left -= page_of_nops.len;
- }
- if (padding_left > 0) {
- vecs[vec_index] = .{
- .iov_base = &page_of_nops,
- .iov_len = padding_left,
- };
- vec_index += 1;
- }
- }
-
- vecs[vec_index] = .{
- .iov_base = buf.ptr,
- .iov_len = buf.len,
- };
- vec_index += 1;
-
- {
- var padding_left = next_padding_size;
- if (padding_left % 2 != 0) {
- vecs[vec_index] = .{
- .iov_base = &three_byte_nop,
- .iov_len = three_byte_nop.len,
- };
- vec_index += 1;
- padding_left -= three_byte_nop.len;
- }
- while (padding_left > page_of_nops.len) {
- vecs[vec_index] = .{
- .iov_base = &page_of_nops,
- .iov_len = page_of_nops.len,
- };
- vec_index += 1;
- padding_left -= page_of_nops.len;
- }
- if (padding_left > 0) {
- vecs[vec_index] = .{
- .iov_base = &page_of_nops,
- .iov_len = padding_left,
- };
- vec_index += 1;
- }
- }
- try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
-}
-
-/// Writes to the file a buffer, prefixed and suffixed by the specified number of
-/// bytes of padding.
-fn pwriteDbgInfoNops(
- self: *Elf,
- prev_padding_size: usize,
- buf: []const u8,
- next_padding_size: usize,
- trailing_zero: bool,
- offset: usize,
-) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const page_of_nops = [1]u8{abbrev_pad1} ** 4096;
- var vecs: [32]std.os.iovec_const = undefined;
- var vec_index: usize = 0;
- {
- var padding_left = prev_padding_size;
- while (padding_left > page_of_nops.len) {
- vecs[vec_index] = .{
- .iov_base = &page_of_nops,
- .iov_len = page_of_nops.len,
- };
- vec_index += 1;
- padding_left -= page_of_nops.len;
- }
- if (padding_left > 0) {
- vecs[vec_index] = .{
- .iov_base = &page_of_nops,
- .iov_len = padding_left,
- };
- vec_index += 1;
- }
- }
-
- vecs[vec_index] = .{
- .iov_base = buf.ptr,
- .iov_len = buf.len,
- };
- vec_index += 1;
-
- {
- var padding_left = next_padding_size;
- while (padding_left > page_of_nops.len) {
- vecs[vec_index] = .{
- .iov_base = &page_of_nops,
- .iov_len = page_of_nops.len,
- };
- vec_index += 1;
- padding_left -= page_of_nops.len;
- }
- if (padding_left > 0) {
- vecs[vec_index] = .{
- .iov_base = &page_of_nops,
- .iov_len = padding_left,
- };
- vec_index += 1;
- }
- }
-
- if (trailing_zero) {
- var zbuf = [1]u8{0};
- vecs[vec_index] = .{
- .iov_base = &zbuf,
- .iov_len = zbuf.len,
- };
- vec_index += 1;
- }
-
- try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
-}
-
-/// Saturating multiplication
-fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
- const T = @TypeOf(a, b);
- return std.math.mul(T, a, b) catch std.math.maxInt(T);
-}
-
-fn bswapAllFields(comptime S: type, ptr: *S) void {
- @panic("TODO implement bswapAllFields");
-}
-
-fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr {
- return .{
- .p_type = phdr.p_type,
- .p_flags = phdr.p_flags,
- .p_offset = @intCast(u32, phdr.p_offset),
- .p_vaddr = @intCast(u32, phdr.p_vaddr),
- .p_paddr = @intCast(u32, phdr.p_paddr),
- .p_filesz = @intCast(u32, phdr.p_filesz),
- .p_memsz = @intCast(u32, phdr.p_memsz),
- .p_align = @intCast(u32, phdr.p_align),
- };
-}
-
-fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
- return .{
- .sh_name = shdr.sh_name,
- .sh_type = shdr.sh_type,
- .sh_flags = @intCast(u32, shdr.sh_flags),
- .sh_addr = @intCast(u32, shdr.sh_addr),
- .sh_offset = @intCast(u32, shdr.sh_offset),
- .sh_size = @intCast(u32, shdr.sh_size),
- .sh_link = shdr.sh_link,
- .sh_info = shdr.sh_info,
- .sh_addralign = @intCast(u32, shdr.sh_addralign),
- .sh_entsize = @intCast(u32, shdr.sh_entsize),
- };
-}
diff --git a/src-self-hosted/link/MachO.zig b/src-self-hosted/link/MachO.zig
deleted file mode 100644
index 13932e514ada267868aba48a750e48220092dd38..0000000000000000000000000000000000000000
--- a/src-self-hosted/link/MachO.zig
+++ /dev/null
@@ -1,733 +0,0 @@
-const MachO = @This();
-
-const std = @import("std");
-const Allocator = std.mem.Allocator;
-const assert = std.debug.assert;
-const fs = std.fs;
-const log = std.log.scoped(.link);
-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 Module = @import("../Module.zig");
-const link = @import("../link.zig");
-const File = link.File;
-
-pub const base_tag: File.Tag = File.Tag.macho;
-
-const LoadCommand = union(enum) {
- Segment: macho.segment_command_64,
- LinkeditData: macho.linkedit_data_command,
- Symtab: macho.symtab_command,
- Dysymtab: macho.dysymtab_command,
-
- pub fn cmdsize(self: LoadCommand) u32 {
- return switch (self) {
- .Segment => |x| x.cmdsize,
- .LinkeditData => |x| x.cmdsize,
- .Symtab => |x| x.cmdsize,
- .Dysymtab => |x| x.cmdsize,
- };
- }
-
- pub fn write(self: LoadCommand, file: *fs.File, offset: u64) !void {
- return switch (self) {
- .Segment => |cmd| writeGeneric(cmd, file, offset),
- .LinkeditData => |cmd| writeGeneric(cmd, file, offset),
- .Symtab => |cmd| writeGeneric(cmd, file, offset),
- .Dysymtab => |cmd| writeGeneric(cmd, file, offset),
- };
- }
-
- fn writeGeneric(cmd: anytype, file: *fs.File, offset: u64) !void {
- const slice = [1]@TypeOf(cmd){cmd};
- return file.pwriteAll(mem.sliceAsBytes(slice[0..1]), offset);
- }
-};
-
-base: File,
-
-/// Table of all load commands
-load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
-segment_cmd_index: ?u16 = null,
-symtab_cmd_index: ?u16 = null,
-dysymtab_cmd_index: ?u16 = null,
-data_in_code_cmd_index: ?u16 = null,
-
-/// Table of all sections
-sections: std.ArrayListUnmanaged(macho.section_64) = .{},
-
-/// __TEXT segment sections
-text_section_index: ?u16 = null,
-cstring_section_index: ?u16 = null,
-const_text_section_index: ?u16 = null,
-stubs_section_index: ?u16 = null,
-stub_helper_section_index: ?u16 = null,
-
-/// __DATA segment sections
-got_section_index: ?u16 = null,
-const_data_section_index: ?u16 = null,
-
-entry_addr: ?u64 = null,
-
-/// Table of all symbols used.
-/// Internally references string table for names (which are optional).
-symbol_table: std.ArrayListUnmanaged(macho.nlist_64) = .{},
-
-/// Table of symbol names aka the string table.
-string_table: std.ArrayListUnmanaged(u8) = .{},
-
-/// Table of symbol vaddr values. The values is the absolute vaddr value.
-/// If the vaddr of the executable __TEXT segment vaddr changes, the entire offset
-/// table needs to be rewritten.
-offset_table: std.ArrayListUnmanaged(u64) = .{},
-
-error_flags: File.ErrorFlags = File.ErrorFlags{},
-
-cmd_table_dirty: bool = false,
-
-/// Pointer to the last allocated text block
-last_text_block: ?*TextBlock = null,
-
-/// `alloc_num / alloc_den` is the factor of padding when allocating.
-const alloc_num = 4;
-const alloc_den = 3;
-
-/// Default path to dyld
-/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
-/// instead but this will do for now.
-const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld";
-
-/// Default lib search path
-/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
-/// instead but this will do for now.
-const DEFAULT_LIB_SEARCH_PATH: []const u8 = "/usr/lib";
-
-const LIB_SYSTEM_NAME: [*:0]const u8 = "System";
-/// TODO we should search for libSystem and fail if it doesn't exist, instead of hardcoding it
-const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib";
-
-pub const TextBlock = struct {
- /// Index into the symbol table
- symbol_table_index: ?u32,
- /// Index into offset table
- offset_table_index: ?u32,
- /// Size of this text block
- size: u64,
- /// Points to the previous and next neighbours
- prev: ?*TextBlock,
- next: ?*TextBlock,
-
- pub const empty = TextBlock{
- .symbol_table_index = null,
- .offset_table_index = null,
- .size = 0,
- .prev = null,
- .next = null,
- };
-};
-
-pub const SrcFn = struct {
- pub const empty = SrcFn{};
-};
-
-pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File {
- assert(options.object_format == .macho);
-
- const file = try dir.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);
-
- macho_file.* = openFile(allocator, file, options) catch |err| switch (err) {
- error.IncrFailed => try createFile(allocator, file, options),
- else => |e| return e,
- };
-
- 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 {
- switch (self.base.options.output_mode) {
- .Exe => {
- var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
- {
- // Specify path to dynamic linker dyld
- const cmdsize = commandSize(@sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH));
- const load_dylinker = [1]macho.dylinker_command{
- .{
- .cmd = macho.LC_LOAD_DYLINKER,
- .cmdsize = cmdsize,
- .name = @sizeOf(macho.dylinker_command),
- },
- };
-
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylinker[0..1]), last_cmd_offset);
-
- const file_offset = last_cmd_offset + @sizeOf(macho.dylinker_command);
- try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset);
-
- try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset);
- last_cmd_offset += cmdsize;
- }
-
- {
- // Link against libSystem
- const cmdsize = commandSize(@sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH));
- // TODO Find a way to work out runtime version from the OS version triple stored in std.Target.
- // In the meantime, we're gonna hardcode to the minimum compatibility version of 1.0.0.
- const min_version = 0x10000;
- const dylib = .{
- .name = @sizeOf(macho.dylib_command),
- .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
- .current_version = min_version,
- .compatibility_version = min_version,
- };
- const load_dylib = [1]macho.dylib_command{
- .{
- .cmd = macho.LC_LOAD_DYLIB,
- .cmdsize = cmdsize,
- .dylib = dylib,
- },
- };
-
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylib[0..1]), last_cmd_offset);
-
- const file_offset = last_cmd_offset + @sizeOf(macho.dylib_command);
- try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset);
-
- try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), file_offset);
- last_cmd_offset += cmdsize;
- }
- },
- .Obj => {
- {
- const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
- symtab.nsyms = @intCast(u32, self.symbol_table.items.len);
- const allocated_size = self.allocatedSize(symtab.stroff);
- const needed_size = self.string_table.items.len;
- log.debug("allocated_size = 0x{x}, needed_size = 0x{x}\n", .{ allocated_size, needed_size });
-
- if (needed_size > allocated_size) {
- symtab.strsize = 0;
- symtab.stroff = @intCast(u32, self.findFreeSpace(needed_size, 1));
- }
- symtab.strsize = @intCast(u32, needed_size);
-
- log.debug("writing string table from 0x{x} to 0x{x}\n", .{ symtab.stroff, symtab.stroff + symtab.strsize });
-
- try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff);
- }
-
- var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
- for (self.load_commands.items) |cmd| {
- try cmd.write(&self.base.file.?, last_cmd_offset);
- last_cmd_offset += cmd.cmdsize();
- }
- const off = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64);
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items), off);
- },
- .Lib => return error.TODOImplementWritingLibFiles,
- }
-
- 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;
- try self.writeMachOHeader();
- }
-}
-
-pub fn deinit(self: *MachO) void {
- self.offset_table.deinit(self.base.allocator);
- self.string_table.deinit(self.base.allocator);
- self.symbol_table.deinit(self.base.allocator);
- self.sections.deinit(self.base.allocator);
- self.load_commands.deinit(self.base.allocator);
-}
-
-pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
- if (decl.link.macho.symbol_table_index) |_| return;
-
- try self.symbol_table.ensureCapacity(self.base.allocator, self.symbol_table.items.len + 1);
- try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
-
- log.debug("allocating symbol index {} for {}\n", .{ self.symbol_table.items.len, decl.name });
- decl.link.macho.symbol_table_index = @intCast(u32, self.symbol_table.items.len);
- _ = self.symbol_table.addOneAssumeCapacity();
-
- decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);
- _ = self.offset_table.addOneAssumeCapacity();
-
- self.symbol_table.items[decl.link.macho.symbol_table_index.?] = .{
- .n_strx = 0,
- .n_type = 0,
- .n_sect = 0,
- .n_desc = 0,
- .n_value = 0,
- };
- self.offset_table.items[decl.link.macho.offset_table_index.?] = 0;
-}
-
-pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
- 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;
- },
- };
- log.debug("generated code {}\n", .{code});
-
- const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
- const symbol = &self.symbol_table.items[decl.link.macho.symbol_table_index.?];
-
- const decl_name = mem.spanZ(decl.name);
- const name_str_index = try self.makeString(decl_name);
- const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment);
- log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, addr });
- log.debug("updated text section {}\n", .{self.sections.items[self.text_section_index.?]});
-
- symbol.* = .{
- .n_strx = name_str_index,
- .n_type = macho.N_SECT,
- .n_sect = @intCast(u8, self.text_section_index.?) + 1,
- .n_desc = 0,
- .n_value = addr,
- };
-
- // 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{};
- try self.updateDeclExports(module, decl, decl_exports);
- try self.writeSymbol(decl.link.macho.symbol_table_index.?);
-
- const text_section = self.sections.items[self.text_section_index.?];
- const section_offset = symbol.n_value - text_section.addr;
- const file_offset = text_section.offset + section_offset;
- log.debug("file_offset 0x{x}\n", .{file_offset});
-
- try self.base.file.?.pwriteAll(code, file_offset);
-}
-
-pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {}
-
-pub fn updateDeclExports(
- self: *MachO,
- module: *Module,
- decl: *const Module.Decl,
- exports: []const *Module.Export,
-) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- if (decl.link.macho.symbol_table_index == null) return;
-
- const decl_sym = &self.symbol_table.items[decl.link.macho.symbol_table_index.?];
- // TODO implement
- if (exports.len == 0) return;
-
- const exp = exports[0];
- self.entry_addr = decl_sym.n_value;
- decl_sym.n_type |= macho.N_EXT;
- exp.link.sym_index = 0;
-}
-
-pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {}
-
-pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
- return self.symbol_table.items[decl.link.macho.symbol_table_index.?].n_value;
-}
-
-pub fn populateMissingMetadata(self: *MachO) !void {
- if (self.segment_cmd_index == null) {
- self.segment_cmd_index = @intCast(u16, self.load_commands.items.len);
- try self.load_commands.append(self.base.allocator, .{
- .Segment = .{
- .cmd = macho.LC_SEGMENT_64,
- .cmdsize = @sizeOf(macho.segment_command_64),
- .segname = makeStaticString(""),
- .vmaddr = 0,
- .vmsize = 0,
- .fileoff = 0,
- .filesize = 0,
- .maxprot = 0,
- .initprot = 0,
- .nsects = 0,
- .flags = 0,
- },
- });
- self.cmd_table_dirty = true;
- }
- if (self.symtab_cmd_index == null) {
- self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
- try self.load_commands.append(self.base.allocator, .{
- .Symtab = .{
- .cmd = macho.LC_SYMTAB,
- .cmdsize = @sizeOf(macho.symtab_command),
- .symoff = 0,
- .nsyms = 0,
- .stroff = 0,
- .strsize = 0,
- },
- });
- self.cmd_table_dirty = true;
- }
- if (self.text_section_index == null) {
- self.text_section_index = @intCast(u16, self.sections.items.len);
- const segment = &self.load_commands.items[self.segment_cmd_index.?].Segment;
- segment.cmdsize += @sizeOf(macho.section_64);
- segment.nsects += 1;
-
- const file_size = self.base.options.program_code_size_hint;
- const off = @intCast(u32, self.findFreeSpace(file_size, 1));
- const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
-
- log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
-
- try self.sections.append(self.base.allocator, .{
- .sectname = makeStaticString("__text"),
- .segname = makeStaticString("__TEXT"),
- .addr = 0,
- .size = file_size,
- .offset = off,
- .@"align" = 0x1000,
- .reloff = 0,
- .nreloc = 0,
- .flags = flags,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
- });
-
- segment.vmsize += file_size;
- segment.filesize += file_size;
- segment.fileoff = off;
-
- log.debug("initial text section {}\n", .{self.sections.items[self.text_section_index.?]});
- }
- {
- const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
- if (symtab.symoff == 0) {
- const p_align = @sizeOf(macho.nlist_64);
- const nsyms = self.base.options.symbol_count_hint;
- const file_size = p_align * nsyms;
- const off = @intCast(u32, self.findFreeSpace(file_size, p_align));
- log.debug("found symbol table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
- symtab.symoff = off;
- symtab.nsyms = @intCast(u32, nsyms);
- }
- if (symtab.stroff == 0) {
- try self.string_table.append(self.base.allocator, 0);
- const file_size = @intCast(u32, self.string_table.items.len);
- const off = @intCast(u32, self.findFreeSpace(file_size, 1));
- log.debug("found string table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
- symtab.stroff = off;
- symtab.strsize = file_size;
- }
- }
-}
-
-fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
- const segment = &self.load_commands.items[self.segment_cmd_index.?].Segment;
- const text_section = &self.sections.items[self.text_section_index.?];
- const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
-
- var block_placement: ?*TextBlock = null;
- const addr = blk: {
- if (self.last_text_block) |last| {
- const last_symbol = self.symbol_table.items[last.symbol_table_index.?];
- const end_addr = last_symbol.n_value + last.size;
- const new_start_addr = mem.alignForwardGeneric(u64, end_addr, alignment);
- block_placement = last;
- break :blk new_start_addr;
- } else {
- break :blk text_section.addr;
- }
- };
- log.debug("computed symbol address 0x{x}\n", .{addr});
-
- const expand_text_section = block_placement == null or block_placement.?.next == null;
- if (expand_text_section) {
- const text_capacity = self.allocatedSize(text_section.offset);
- const needed_size = (addr + new_block_size) - text_section.addr;
- log.debug("text capacity 0x{x}, needed size 0x{x}\n", .{ text_capacity, needed_size });
- assert(needed_size <= text_capacity); // TODO handle growth
-
- self.last_text_block = text_block;
- text_section.size = needed_size;
- segment.vmsize = needed_size;
- segment.filesize = needed_size;
- if (alignment < text_section.@"align") {
- text_section.@"align" = @intCast(u32, alignment);
- }
- }
- text_block.size = new_block_size;
-
- 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;
- }
-
- return addr;
-}
-
-fn makeStaticString(comptime bytes: []const u8) [16]u8 {
- var buf = [_]u8{0} ** 16;
- if (bytes.len > buf.len) @compileError("string too long; max 16 bytes");
- mem.copy(u8, buf[0..], bytes);
- return buf;
-}
-
-fn makeString(self: *MachO, bytes: []const u8) !u32 {
- try self.string_table.ensureCapacity(self.base.allocator, self.string_table.items.len + bytes.len + 1);
- const result = self.string_table.items.len;
- self.string_table.appendSliceAssumeCapacity(bytes);
- self.string_table.appendAssumeCapacity(0);
- return @intCast(u32, result);
-}
-
-fn alignSize(comptime Int: type, min_size: anytype, alignment: Int) Int {
- const size = @intCast(Int, min_size);
- if (size % alignment == 0) return size;
-
- const div = size / alignment;
- return (div + 1) * alignment;
-}
-
-fn commandSize(min_size: anytype) u32 {
- return alignSize(u32, min_size, @sizeOf(u64));
-}
-
-fn addPadding(self: *MachO, size: u64, file_offset: u64) !void {
- if (size == 0) return;
-
- const buf = try self.base.allocator.alloc(u8, size);
- defer self.base.allocator.free(buf);
-
- mem.set(u8, buf[0..], 0);
-
- try self.base.file.?.pwriteAll(buf, file_offset);
-}
-
-fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
- const hdr_size: u64 = @sizeOf(macho.mach_header_64);
- if (start < hdr_size)
- return hdr_size;
-
- const end = start + satMul(size, alloc_num) / alloc_den;
-
- {
- const off = @sizeOf(macho.mach_header_64);
- var tight_size: u64 = 0;
- for (self.load_commands.items) |cmd| {
- tight_size += cmd.cmdsize();
- }
- const increased_size = satMul(tight_size, alloc_num) / alloc_den;
- const test_end = off + increased_size;
- if (end > off and start < test_end) {
- return test_end;
- }
- }
-
- for (self.sections.items) |section| {
- const increased_size = satMul(section.size, alloc_num) / alloc_den;
- const test_end = section.offset + increased_size;
- if (end > section.offset and start < test_end) {
- return test_end;
- }
- }
-
- if (self.symtab_cmd_index) |symtab_index| {
- const symtab = self.load_commands.items[symtab_index].Symtab;
- {
- const tight_size = @sizeOf(macho.nlist_64) * symtab.nsyms;
- const increased_size = satMul(tight_size, alloc_num) / alloc_den;
- const test_end = symtab.symoff + increased_size;
- if (end > symtab.symoff and start < test_end) {
- return test_end;
- }
- }
- {
- const increased_size = satMul(symtab.strsize, alloc_num) / alloc_den;
- const test_end = symtab.stroff + increased_size;
- if (end > symtab.stroff and start < test_end) {
- return test_end;
- }
- }
- }
-
- return null;
-}
-
-fn allocatedSize(self: *MachO, start: u64) u64 {
- if (start == 0)
- return 0;
- var min_pos: u64 = std.math.maxInt(u64);
- {
- const off = @sizeOf(macho.mach_header_64);
- if (off > start and off < min_pos) min_pos = off;
- }
- for (self.sections.items) |section| {
- if (section.offset <= start) continue;
- if (section.offset < min_pos) min_pos = section.offset;
- }
- if (self.symtab_cmd_index) |symtab_index| {
- const symtab = self.load_commands.items[symtab_index].Symtab;
- if (symtab.symoff > start and symtab.symoff < min_pos) min_pos = symtab.symoff;
- if (symtab.stroff > start and symtab.stroff < min_pos) min_pos = symtab.stroff;
- }
- return min_pos - start;
-}
-
-fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u16) u64 {
- var start: u64 = 0;
- while (self.detectAllocCollision(start, object_size)) |item_end| {
- start = mem.alignForwardGeneric(u64, item_end, min_alignment);
- }
- return start;
-}
-
-fn writeSymbol(self: *MachO, index: usize) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
- const sym = [1]macho.nlist_64{self.symbol_table.items[index]};
- const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
- log.debug("writing symbol {} at 0x{x}\n", .{ sym[0], off });
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
-}
-
-/// Writes Mach-O file header.
-/// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping
-/// variables.
-fn writeMachOHeader(self: *MachO) !void {
- var hdr: macho.mach_header_64 = undefined;
- hdr.magic = macho.MH_MAGIC_64;
-
- const CpuInfo = struct {
- cpu_type: macho.cpu_type_t,
- cpu_subtype: macho.cpu_subtype_t,
- };
-
- const cpu_info: CpuInfo = switch (self.base.options.target.cpu.arch) {
- .aarch64 => .{
- .cpu_type = macho.CPU_TYPE_ARM64,
- .cpu_subtype = macho.CPU_SUBTYPE_ARM_ALL,
- },
- .x86_64 => .{
- .cpu_type = macho.CPU_TYPE_X86_64,
- .cpu_subtype = macho.CPU_SUBTYPE_X86_64_ALL,
- },
- else => return error.UnsupportedMachOArchitecture,
- };
- hdr.cputype = cpu_info.cpu_type;
- hdr.cpusubtype = cpu_info.cpu_subtype;
-
- const filetype: u32 = switch (self.base.options.output_mode) {
- .Exe => macho.MH_EXECUTE,
- .Obj => macho.MH_OBJECT,
- .Lib => switch (self.base.options.link_mode) {
- .Static => return error.TODOStaticLibMachOType,
- .Dynamic => macho.MH_DYLIB,
- },
- };
- hdr.filetype = filetype;
- hdr.ncmds = @intCast(u32, self.load_commands.items.len);
-
- var sizeofcmds: u32 = 0;
- for (self.load_commands.items) |cmd| {
- sizeofcmds += cmd.cmdsize();
- }
-
- hdr.sizeofcmds = sizeofcmds;
-
- // TODO should these be set to something else?
- hdr.flags = 0;
- hdr.reserved = 0;
-
- log.debug("writing Mach-O header {}\n", .{hdr});
-
- try self.base.file.?.pwriteAll(@ptrCast([*]const u8, &hdr)[0..@sizeOf(macho.mach_header_64)], 0);
-}
-
-/// Saturating multiplication
-fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
- const T = @TypeOf(a, b);
- return std.math.mul(T, a, b) catch std.math.maxInt(T);
-}
diff --git a/src-self-hosted/link/Wasm.zig b/src-self-hosted/link/Wasm.zig
deleted file mode 100644
index d8f172f584f26446c28ed0ede3e1874cabf8ca42..0000000000000000000000000000000000000000
--- 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/link/cbe.h b/src-self-hosted/link/cbe.h
deleted file mode 100644
index 854032227d1aed97b78f359c282efecb0f5f5e8b..0000000000000000000000000000000000000000
--- a/src-self-hosted/link/cbe.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#if __STDC_VERSION__ >= 201112L
-#define zig_noreturn _Noreturn
-#elif __GNUC__
-#define zig_noreturn __attribute__ ((noreturn))
-#elif _MSC_VER
-#define zig_noreturn __declspec(noreturn)
-#else
-#define zig_noreturn
-#endif
-
-#if __GNUC__
-#define zig_unreachable() __builtin_unreachable()
-#else
-#define zig_unreachable()
-#endif
diff --git a/src-self-hosted/link/msdos-stub.bin b/src-self-hosted/link/msdos-stub.bin
deleted file mode 100644
index 96ad91198f0de1eb25b9d9846c44706823dffa58..0000000000000000000000000000000000000000
Binary files a/src-self-hosted/link/msdos-stub.bin and /dev/null differ
diff --git a/src-self-hosted/liveness.zig b/src-self-hosted/liveness.zig
deleted file mode 100644
index d528e09ce7b85cea0ed90f7919e1e8b7cc0a4866..0000000000000000000000000000000000000000
--- a/src-self-hosted/liveness.zig
+++ /dev/null
@@ -1,166 +0,0 @@
-const std = @import("std");
-const ir = @import("ir.zig");
-const trace = @import("tracy.zig").trace;
-
-/// Perform Liveness Analysis over the `Body`. Each `Inst` will have its `deaths` field populated.
-pub fn analyze(
- /// Used for temporary storage during the analysis.
- gpa: *std.mem.Allocator,
- /// Used to tack on extra allocations in the same lifetime as the existing instructions.
- arena: *std.mem.Allocator,
- body: ir.Body,
-) error{OutOfMemory}!void {
- const tracy = trace(@src());
- defer tracy.end();
-
- var table = std.AutoHashMap(*ir.Inst, void).init(gpa);
- defer table.deinit();
- try table.ensureCapacity(@intCast(u32, body.instructions.len));
- try analyzeWithTable(arena, &table, null, body);
-}
-
-fn analyzeWithTable(
- arena: *std.mem.Allocator,
- table: *std.AutoHashMap(*ir.Inst, void),
- new_set: ?*std.AutoHashMap(*ir.Inst, void),
- body: ir.Body,
-) error{OutOfMemory}!void {
- var i: usize = body.instructions.len;
-
- if (new_set) |ns| {
- // We are only interested in doing this for instructions which are born
- // before a conditional branch, so after obtaining the new set for
- // each branch we prune the instructions which were born within.
- while (i != 0) {
- i -= 1;
- const base = body.instructions[i];
- _ = ns.remove(base);
- try analyzeInst(arena, table, new_set, base);
- }
- } else {
- while (i != 0) {
- i -= 1;
- const base = body.instructions[i];
- try analyzeInst(arena, table, new_set, base);
- }
- }
-}
-
-fn analyzeInst(
- arena: *std.mem.Allocator,
- table: *std.AutoHashMap(*ir.Inst, void),
- new_set: ?*std.AutoHashMap(*ir.Inst, void),
- base: *ir.Inst,
-) error{OutOfMemory}!void {
- if (table.contains(base)) {
- base.deaths = 0;
- } else {
- // No tombstone for this instruction means it is never referenced,
- // and its birth marks its own death. Very metal 🤘
- base.deaths = 1 << ir.Inst.unreferenced_bit_index;
- }
-
- switch (base.tag) {
- .constant => return,
- .block => {
- const inst = base.castTag(.block).?;
- try analyzeWithTable(arena, table, new_set, inst.body);
- // We let this continue so that it can possibly mark the block as
- // unreferenced below.
- },
- .loop => {
- const inst = base.castTag(.loop).?;
- try analyzeWithTable(arena, table, new_set, inst.body);
- return; // Loop has no operands and it is always unreferenced.
- },
- .condbr => {
- const inst = base.castTag(.condbr).?;
-
- // Each death that occurs inside one branch, but not the other, needs
- // to be added as a death immediately upon entering the other branch.
-
- var then_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
- defer then_table.deinit();
- try analyzeWithTable(arena, table, &then_table, inst.then_body);
-
- // Reset the table back to its state from before the branch.
- {
- var it = then_table.iterator();
- while (it.next()) |entry| {
- table.removeAssertDiscard(entry.key);
- }
- }
-
- var else_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
- defer else_table.deinit();
- try analyzeWithTable(arena, table, &else_table, inst.else_body);
-
- var then_entry_deaths = std.ArrayList(*ir.Inst).init(table.allocator);
- defer then_entry_deaths.deinit();
- var else_entry_deaths = std.ArrayList(*ir.Inst).init(table.allocator);
- defer else_entry_deaths.deinit();
-
- {
- var it = else_table.iterator();
- while (it.next()) |entry| {
- const else_death = entry.key;
- if (!then_table.contains(else_death)) {
- try then_entry_deaths.append(else_death);
- }
- }
- }
- // This loop is the same, except it's for the then branch, and it additionally
- // has to put its items back into the table to undo the reset.
- {
- var it = then_table.iterator();
- while (it.next()) |entry| {
- const then_death = entry.key;
- if (!else_table.contains(then_death)) {
- try else_entry_deaths.append(then_death);
- }
- _ = try table.put(then_death, {});
- }
- }
- // Now we have to correctly populate new_set.
- if (new_set) |ns| {
- try ns.ensureCapacity(@intCast(u32, ns.count() + then_table.count() + else_table.count()));
- var it = then_table.iterator();
- while (it.next()) |entry| {
- _ = ns.putAssumeCapacity(entry.key, {});
- }
- it = else_table.iterator();
- while (it.next()) |entry| {
- _ = ns.putAssumeCapacity(entry.key, {});
- }
- }
- inst.then_death_count = std.math.cast(@TypeOf(inst.then_death_count), then_entry_deaths.items.len) catch return error.OutOfMemory;
- inst.else_death_count = std.math.cast(@TypeOf(inst.else_death_count), else_entry_deaths.items.len) catch return error.OutOfMemory;
- const allocated_slice = try arena.alloc(*ir.Inst, then_entry_deaths.items.len + else_entry_deaths.items.len);
- inst.deaths = allocated_slice.ptr;
- std.mem.copy(*ir.Inst, inst.thenDeaths(), then_entry_deaths.items);
- std.mem.copy(*ir.Inst, inst.elseDeaths(), else_entry_deaths.items);
-
- // Continue on with the instruction analysis. The following code will find the condition
- // instruction, and the deaths flag for the CondBr instruction will indicate whether the
- // condition's lifetime ends immediately before entering any branch.
- },
- else => {},
- }
-
- const needed_bits = base.operandCount();
- if (needed_bits <= ir.Inst.deaths_bits) {
- var bit_i: ir.Inst.DeathsBitIndex = 0;
- while (base.getOperand(bit_i)) |operand| : (bit_i += 1) {
- const prev = try table.fetchPut(operand, {});
- if (prev == null) {
- // Death.
- base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
- if (new_set) |ns| try ns.putNoClobber(operand, {});
- }
- }
- } else {
- @panic("Handle liveness analysis for instructions with many parameters");
- }
-
- std.log.scoped(.liveness).debug("analyze {}: 0b{b}\n", .{ base.tag, base.deaths });
-}
diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig
deleted file mode 100644
index 41599682526f37bb0a26f1a6d545ae4bf53c9a46..0000000000000000000000000000000000000000
--- 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 fb20a09f1dabe8c249a3a4fd9fc6e98ac20e5dcb..0000000000000000000000000000000000000000
--- 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/print_env.zig b/src-self-hosted/print_env.zig
deleted file mode 100644
index 9b68633d3ef3c4d3138dc94f69887801a8f79484..0000000000000000000000000000000000000000
--- a/src-self-hosted/print_env.zig
+++ /dev/null
@@ -1,47 +0,0 @@
-const std = @import("std");
-const build_options = @import("build_options");
-const introspect = @import("introspect.zig");
-const Allocator = std.mem.Allocator;
-
-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 zig_std_dir = try std.fs.path.join(gpa, &[_][]const u8{ zig_lib_dir, "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("lib_dir");
- try jws.emitString(zig_lib_dir);
-
- 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);
-
- try jws.objectField("version");
- try jws.emitString(build_options.version);
-
- try jws.endObject();
- try bos_stream.writeByte('\n');
- try bos.flush();
-}
diff --git a/src-self-hosted/print_targets.zig b/src-self-hosted/print_targets.zig
deleted file mode 100644
index 0fe755ffb414d28f8f42f5320c7e6591fbf69030..0000000000000000000000000000000000000000
--- a/src-self-hosted/print_targets.zig
+++ /dev/null
@@ -1,225 +0,0 @@
-const std = @import("std");
-const fs = std.fs;
-const io = std.io;
-const mem = std.mem;
-const Allocator = mem.Allocator;
-const Target = std.Target;
-const assert = std.debug.assert;
-
-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",
-};
-
-pub fn cmdTargets(
- allocator: *Allocator,
- args: []const []const u8,
- /// Output stream
- 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();
- };
- defer allocator.free(available_glibcs);
-
- var bos = 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("arch");
- try jws.beginArray();
- {
- inline for (@typeInfo(Target.Cpu.Arch).Enum.fields) |field| {
- try jws.arrayElem();
- try jws.emitString(field.name);
- }
- }
- try jws.endArray();
-
- try jws.objectField("os");
- try jws.beginArray();
- inline for (@typeInfo(Target.Os.Tag).Enum.fields) |field| {
- try jws.arrayElem();
- try jws.emitString(field.name);
- }
- try jws.endArray();
-
- try jws.objectField("abi");
- try jws.beginArray();
- inline for (@typeInfo(Target.Abi).Enum.fields) |field| {
- try jws.arrayElem();
- try jws.emitString(field.name);
- }
- try jws.endArray();
-
- try jws.objectField("libc");
- try jws.beginArray();
- for (available_libcs) |libc| {
- try jws.arrayElem();
- try jws.emitString(libc);
- }
- try jws.endArray();
-
- try jws.objectField("glibc");
- try jws.beginArray();
- for (available_glibcs) |glibc| {
- try jws.arrayElem();
-
- const tmp = try std.fmt.allocPrint(allocator, "{}", .{glibc});
- defer allocator.free(tmp);
- try jws.emitString(tmp);
- }
- try jws.endArray();
-
- try jws.objectField("cpus");
- try jws.beginObject();
- inline for (@typeInfo(Target.Cpu.Arch).Enum.fields) |field| {
- try jws.objectField(field.name);
- try jws.beginObject();
- const arch = @field(Target.Cpu.Arch, field.name);
- for (arch.allCpuModels()) |model| {
- try jws.objectField(model.name);
- try jws.beginArray();
- for (arch.allFeaturesList()) |feature, i| {
- if (model.features.isEnabled(@intCast(u8, i))) {
- try jws.arrayElem();
- try jws.emitString(feature.name);
- }
- }
- try jws.endArray();
- }
- try jws.endObject();
- }
- try jws.endObject();
-
- try jws.objectField("cpuFeatures");
- try jws.beginObject();
- inline for (@typeInfo(Target.Cpu.Arch).Enum.fields) |field| {
- try jws.objectField(field.name);
- try jws.beginArray();
- const arch = @field(Target.Cpu.Arch, field.name);
- for (arch.allFeaturesList()) |feature| {
- try jws.arrayElem();
- try jws.emitString(feature.name);
- }
- try jws.endArray();
- }
- try jws.endObject();
-
- try jws.objectField("native");
- try jws.beginObject();
- {
- const triple = try native_target.zigTriple(allocator);
- defer allocator.free(triple);
- try jws.objectField("triple");
- try jws.emitString(triple);
- }
- {
- try jws.objectField("cpu");
- try jws.beginObject();
- try jws.objectField("arch");
- try jws.emitString(@tagName(native_target.cpu.arch));
-
- try jws.objectField("name");
- const cpu = native_target.cpu;
- try jws.emitString(cpu.model.name);
-
- {
- try jws.objectField("features");
- try jws.beginArray();
- for (native_target.cpu.arch.allFeaturesList()) |feature, i_usize| {
- const index = @intCast(Target.Cpu.Feature.Set.Index, i_usize);
- if (cpu.features.isEnabled(index)) {
- try jws.arrayElem();
- try jws.emitString(feature.name);
- }
- }
- try jws.endArray();
- }
- try jws.endObject();
- }
- try jws.objectField("os");
- 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();
-
- try bos_stream.writeByte('\n');
- return bos.flush();
-}
diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig
deleted file mode 100644
index ac4d89bb211a4ab70ec03e2b844da7db7c63006b..0000000000000000000000000000000000000000
--- 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-self-hosted/test.zig b/src-self-hosted/test.zig
deleted file mode 100644
index aef48e198be91cd029427e91deccdf6dddcc922a..0000000000000000000000000000000000000000
--- a/src-self-hosted/test.zig
+++ /dev/null
@@ -1,773 +0,0 @@
-const std = @import("std");
-const link = @import("link.zig");
-const Module = @import("Module.zig");
-const Allocator = std.mem.Allocator;
-const zir = @import("zir.zig");
-const Package = @import("Package.zig");
-const build_options = @import("build_options");
-const enable_qemu: bool = build_options.enable_qemu;
-const enable_wine: bool = build_options.enable_wine;
-const enable_wasmtime: bool = build_options.enable_wasmtime;
-const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_dir;
-
-const cheader = @embedFile("link/cbe.h");
-
-test "self-hosted" {
- var ctx = TestContext.init();
- defer ctx.deinit();
-
- try @import("stage2_tests").addCases(&ctx);
-
- try ctx.run();
-}
-
-const ErrorMsg = struct {
- msg: []const u8,
- line: u32,
- column: u32,
-};
-
-pub const TestContext = struct {
- /// TODO: find a way to treat cases as individual tests (shouldn't show "1 test passed" if there are 200 cases)
- cases: std.ArrayList(Case),
-
- pub const Update = struct {
- /// The input to the current update. We simulate an incremental update
- /// with the file's contents changed to this value each update.
- ///
- /// This value can change entirely between updates, which would be akin
- /// to deleting the source file and creating a new one from scratch; or
- /// you can keep it mostly consistent, with small changes, testing the
- /// effects of the incremental compilation.
- src: [:0]const u8,
- case: union(enum) {
- /// A transformation update transforms the input and tests against
- /// the expected output ZIR.
- Transformation: [:0]const u8,
- /// An error update attempts to compile bad code, and ensures that it
- /// fails to compile, and for the expected reasons.
- /// A slice containing the expected errors *in sequential order*.
- Error: []const ErrorMsg,
- /// An execution update compiles and runs the input, testing the
- /// stdout against the expected results
- /// This is a slice containing the expected message.
- Execution: []const u8,
- },
- };
-
- pub const TestType = enum {
- Zig,
- ZIR,
- };
-
- /// A Case consists of a set of *updates*. The same Module 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 {
- /// The name of the test case. This is shown if a test fails, and
- /// otherwise ignored.
- name: []const u8,
- /// The platform the test targets. For non-native platforms, an emulator
- /// such as QEMU is required for tests to complete.
- target: std.zig.CrossTarget,
- /// In order to be able to run e.g. Execution updates, this must be set
- /// to Executable.
- output_mode: std.builtin.OutputMode,
- updates: std.ArrayList(Update),
- extension: TestType,
- cbe: bool = false,
-
- /// Adds a subcase in which the module is updated with `src`, and the
- /// resulting ZIR is validated against `result`.
- pub fn addTransform(self: *Case, src: [:0]const u8, result: [:0]const u8) void {
- self.updates.append(.{
- .src = src,
- .case = .{ .Transformation = result },
- }) catch unreachable;
- }
-
- /// Adds a subcase in which the module is updated with `src`, compiled,
- /// run, and the output is tested against `result`.
- pub fn addCompareOutput(self: *Case, src: [:0]const u8, result: []const u8) void {
- self.updates.append(.{
- .src = src,
- .case = .{ .Execution = result },
- }) catch unreachable;
- }
-
- /// Adds a subcase in which the module is updated with `src`, which
- /// should contain invalid input, and ensures that compilation fails
- /// for the expected reasons, given in sequential order in `errors` in
- /// the form `:line:column: error: message`.
- pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void {
- var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch unreachable;
- for (errors) |e, i| {
- if (e[0] != ':') {
- @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n");
- }
- var cur = e[1..];
- var line_index = std.mem.indexOf(u8, cur, ":");
- if (line_index == null) {
- @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n");
- }
- const line = std.fmt.parseInt(u32, cur[0..line_index.?], 10) catch @panic("Unable to parse line number");
- cur = cur[line_index.? + 1 ..];
- const column_index = std.mem.indexOf(u8, cur, ":");
- if (column_index == null) {
- @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n");
- }
- const column = std.fmt.parseInt(u32, cur[0..column_index.?], 10) catch @panic("Unable to parse column number");
- cur = cur[column_index.? + 2 ..];
- if (!std.mem.eql(u8, cur[0..7], "error: ")) {
- @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n");
- }
- const msg = cur[7..];
-
- if (line == 0 or column == 0) {
- @panic("Invalid test: error line and column must be specified starting at one!");
- }
-
- array[i] = .{
- .msg = msg,
- .line = line - 1,
- .column = column - 1,
- };
- }
- self.updates.append(.{ .src = src, .case = .{ .Error = array } }) catch unreachable;
- }
-
- /// Adds a subcase in which the module is updated with `src`, and
- /// asserts that it compiles without issue
- pub fn compiles(self: *Case, src: [:0]const u8) void {
- self.addError(src, &[_][]const u8{});
- }
- };
-
- pub fn addExe(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- T: TestType,
- ) *Case {
- ctx.cases.append(Case{
- .name = name,
- .target = target,
- .updates = std.ArrayList(Update).init(ctx.cases.allocator),
- .output_mode = .Exe,
- .extension = T,
- }) catch unreachable;
- return &ctx.cases.items[ctx.cases.items.len - 1];
- }
-
- /// Adds a test case for Zig input, producing an executable
- pub fn exe(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {
- return ctx.addExe(name, target, .Zig);
- }
-
- /// Adds a test case for ZIR input, producing an executable
- pub fn exeZIR(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {
- return ctx.addExe(name, target, .ZIR);
- }
-
- pub fn addObj(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- T: TestType,
- ) *Case {
- ctx.cases.append(Case{
- .name = name,
- .target = target,
- .updates = std.ArrayList(Update).init(ctx.cases.allocator),
- .output_mode = .Obj,
- .extension = T,
- }) catch unreachable;
- return &ctx.cases.items[ctx.cases.items.len - 1];
- }
-
- /// Adds a test case for Zig input, producing an object file
- pub fn obj(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {
- return ctx.addObj(name, target, .Zig);
- }
-
- /// Adds a test case for ZIR input, producing an object file
- pub fn objZIR(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {
- return ctx.addObj(name, target, .ZIR);
- }
-
- pub fn addC(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, T: TestType) *Case {
- ctx.cases.append(Case{
- .name = name,
- .target = target,
- .updates = std.ArrayList(Update).init(ctx.cases.allocator),
- .output_mode = .Obj,
- .extension = T,
- .cbe = true,
- }) catch unreachable;
- return &ctx.cases.items[ctx.cases.items.len - 1];
- }
-
- pub fn c(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void {
- ctx.addC(name, target, .Zig).addTransform(src, cheader ++ out);
- }
-
- pub fn addCompareOutput(
- ctx: *TestContext,
- name: []const u8,
- T: TestType,
- src: [:0]const u8,
- expected_stdout: []const u8,
- ) void {
- ctx.addExe(name, .{}, T).addCompareOutput(src, expected_stdout);
- }
-
- /// Adds a test case that compiles the Zig source given in `src`, executes
- /// it, runs it, and tests the output against `expected_stdout`
- pub fn compareOutput(
- ctx: *TestContext,
- name: []const u8,
- src: [:0]const u8,
- expected_stdout: []const u8,
- ) void {
- return ctx.addCompareOutput(name, .Zig, src, expected_stdout);
- }
-
- /// Adds a test case that compiles the ZIR source given in `src`, executes
- /// it, runs it, and tests the output against `expected_stdout`
- pub fn compareOutputZIR(
- ctx: *TestContext,
- name: []const u8,
- src: [:0]const u8,
- expected_stdout: []const u8,
- ) void {
- ctx.addCompareOutput(name, .ZIR, src, expected_stdout);
- }
-
- pub fn addTransform(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- T: TestType,
- src: [:0]const u8,
- result: [:0]const u8,
- ) void {
- ctx.addObj(name, target, T).addTransform(src, result);
- }
-
- /// Adds a test case that compiles the Zig given in `src` to ZIR and tests
- /// the ZIR against `result`
- pub fn transform(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- src: [:0]const u8,
- result: [:0]const u8,
- ) void {
- ctx.addTransform(name, target, .Zig, src, result);
- }
-
- /// Adds a test case that cleans up the ZIR source given in `src`, and
- /// tests the resulting ZIR against `result`
- pub fn transformZIR(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- src: [:0]const u8,
- result: [:0]const u8,
- ) void {
- ctx.addTransform(name, target, .ZIR, src, result);
- }
-
- pub fn addError(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- T: TestType,
- src: [:0]const u8,
- expected_errors: []const []const u8,
- ) void {
- ctx.addObj(name, target, T).addError(src, expected_errors);
- }
-
- /// Adds a test case that ensures that the Zig given in `src` fails to
- /// compile for the expected reasons, given in sequential order in
- /// `expected_errors` in the form `:line:column: error: message`.
- pub fn compileError(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- src: [:0]const u8,
- expected_errors: []const []const u8,
- ) void {
- ctx.addError(name, target, .Zig, src, expected_errors);
- }
-
- /// Adds a test case that ensures that the ZIR given in `src` fails to
- /// compile for the expected reasons, given in sequential order in
- /// `expected_errors` in the form `:line:column: error: message`.
- pub fn compileErrorZIR(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- src: [:0]const u8,
- expected_errors: []const []const u8,
- ) void {
- ctx.addError(name, target, .ZIR, src, expected_errors);
- }
-
- pub fn addCompiles(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- T: TestType,
- src: [:0]const u8,
- ) void {
- ctx.addObj(name, target, T).compiles(src);
- }
-
- /// Adds a test case that asserts that the Zig given in `src` compiles
- /// without any errors.
- pub fn compiles(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- src: [:0]const u8,
- ) void {
- ctx.addCompiles(name, target, .Zig, src);
- }
-
- /// Adds a test case that asserts that the ZIR given in `src` compiles
- /// without any errors.
- pub fn compilesZIR(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- src: [:0]const u8,
- ) void {
- ctx.addCompiles(name, target, .ZIR, src);
- }
-
- /// Adds a test case that first ensures that the Zig given in `src` fails
- /// to compile for the reasons given in sequential order in
- /// `expected_errors` in the form `:line:column: error: message`, then
- /// asserts that fixing the source (updating with `fixed_src`) isn't broken
- /// by incremental compilation.
- pub fn incrementalFailure(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- src: [:0]const u8,
- expected_errors: []const []const u8,
- fixed_src: [:0]const u8,
- ) void {
- var case = ctx.addObj(name, target, .Zig);
- case.addError(src, expected_errors);
- case.compiles(fixed_src);
- }
-
- /// Adds a test case that first ensures that the ZIR given in `src` fails
- /// to compile for the reasons given in sequential order in
- /// `expected_errors` in the form `:line:column: error: message`, then
- /// asserts that fixing the source (updating with `fixed_src`) isn't broken
- /// by incremental compilation.
- pub fn incrementalFailureZIR(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- src: [:0]const u8,
- expected_errors: []const []const u8,
- fixed_src: [:0]const u8,
- ) void {
- var case = ctx.addObj(name, target, .ZIR);
- case.addError(src, expected_errors);
- case.compiles(fixed_src);
- }
-
- fn init() TestContext {
- const allocator = std.heap.page_allocator;
- return .{ .cases = std.ArrayList(Case).init(allocator) };
- }
-
- fn deinit(self: *TestContext) void {
- for (self.cases.items) |case| {
- for (case.updates.items) |u| {
- if (u.case == .Error) {
- case.updates.allocator.free(u.case.Error);
- }
- }
- case.updates.deinit();
- }
- self.cases.deinit();
- self.* = undefined;
- }
-
- fn run(self: *TestContext) !void {
- var progress = std.Progress{};
- const root_node = try progress.start("tests", self.cases.items.len);
- defer root_node.end();
-
- for (self.cases.items) |case| {
- var prg_node = root_node.start(case.name, case.updates.items.len);
- prg_node.activate();
- defer prg_node.end();
-
- // So that we can see which test case failed when the leak checker goes off,
- // or there's an internal error
- progress.initial_delay_ns = 0;
- progress.refresh_rate_ns = 0;
-
- try self.runOneCase(std.testing.allocator, &prg_node, case);
- }
- }
-
- fn runOneCase(self: *TestContext, allocator: *Allocator, root_node: *std.Progress.Node, case: Case) !void {
- const target_info = try std.zig.system.NativeTargetInfo.detect(allocator, case.target);
- const target = target_info.target;
-
- var arena_allocator = std.heap.ArenaAllocator.init(allocator);
- defer arena_allocator.deinit();
- const arena = &arena_allocator.allocator;
-
- 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();
-
- const bin_name = try std.zig.binNameAlloc(arena, "test_case", target, case.output_mode, null);
-
- var module = try Module.init(allocator, .{
- .root_name = "test_case",
- .target = target,
- // TODO: support tests for object file building, and library builds
- // and linking. This will require a rework to support multi-file
- // tests.
- .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,
- .keep_source_files_loaded = true,
- .object_format = if (case.cbe) .c else null,
- });
- defer module.deinit();
-
- for (case.updates.items) |update, update_index| {
- var update_node = root_node.start("update", 3);
- update_node.activate();
- defer update_node.end();
-
- var sync_node = update_node.start("write", null);
- sync_node.activate();
- try tmp.dir.writeFile(tmp_src_path, update.src);
- sync_node.end();
-
- var module_node = update_node.start("parse/analysis/codegen", null);
- module_node.activate();
- try module.makeBinFileWritable();
- try module.update();
- module_node.end();
-
- if (update.case != .Error) {
- var all_errors = try module.getAllErrorsAlloc();
- defer all_errors.deinit(allocator);
- if (all_errors.list.len != 0) {
- std.debug.print("\nErrors occurred updating the module:\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).?;
- std.debug.print("Generated C: \n===============\n{}\n\n===========\n\n", .{C.main.items});
- }
- std.debug.print("Test failed.\n", .{});
- std.process.exit(1);
- }
- }
-
- switch (update.case) {
- .Transformation => |expected_output| {
- if (case.cbe) {
- // The C file is always closed after an update, because we don't support
- // incremental updates
- var file = try tmp.dir.openFile(bin_name, .{ .read = true });
- defer file.close();
- var out = file.reader().readAllAlloc(arena, 1024 * 1024) catch @panic("Unable to read C output!");
-
- if (expected_output.len != out.len) {
- std.debug.print("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
- std.process.exit(1);
- }
- for (expected_output) |e, i| {
- if (out[i] != e) {
- std.debug.print("\nTransformed C differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
- std.process.exit(1);
- }
- }
- } else {
- 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);
- defer new_zir_module.deinit(allocator);
- emit_node.end();
-
- var write_node = update_node.start("write", null);
- write_node.activate();
- var out_zir = std.ArrayList(u8).init(allocator);
- defer out_zir.deinit();
- try new_zir_module.writeToStream(allocator, out_zir.outStream());
- write_node.end();
-
- var test_node = update_node.start("assert", null);
- test_node.activate();
- defer test_node.end();
-
- if (expected_output.len != out_zir.items.len) {
- std.debug.print("{}\nTransformed ZIR length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
- std.process.exit(1);
- }
- for (expected_output) |e, i| {
- if (out_zir.items[i] != e) {
- std.debug.print("{}\nTransformed ZIR differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
- std.process.exit(1);
- }
- }
- }
- },
- .Error => |e| {
- var test_node = update_node.start("assert", null);
- test_node.activate();
- defer test_node.end();
- var handled_errors = try arena.alloc(bool, e.len);
- for (handled_errors) |*h| {
- h.* = false;
- }
- var all_errors = try module.getAllErrorsAlloc();
- defer all_errors.deinit(allocator);
- for (all_errors.list) |a| {
- for (e) |ex, i| {
- if (a.line == ex.line and a.column == ex.column and std.mem.eql(u8, ex.msg, a.msg)) {
- handled_errors[i] = true;
- break;
- }
- } else {
- std.debug.print("{}\nUnexpected error:\n================\n:{}:{}: error: {}\n================\nTest failed.\n", .{ case.name, a.line + 1, a.column + 1, a.msg });
- std.process.exit(1);
- }
- }
-
- for (handled_errors) |h, i| {
- if (!h) {
- const er = e[i];
- std.debug.print("{}\nDid not receive error:\n================\n{}:{}: {}\n================\nTest failed.\n", .{ case.name, er.line, er.column, er.msg });
- std.process.exit(1);
- }
- }
- },
- .Execution => |expected_stdout| {
- std.debug.assert(!case.cbe);
-
- update_node.estimated_total_items = 4;
- var exec_result = x: {
- var exec_node = update_node.start("execute", null);
- exec_node.activate();
- defer exec_node.end();
-
- var argv = std.ArrayList([]const u8).init(allocator);
- defer argv.deinit();
-
- const exe_path = try std.fmt.allocPrint(arena, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name});
-
- switch (case.target.getExternalExecutor()) {
- .native => try argv.append(exe_path),
- .unavailable => {
- try self.runInterpreterIfAvailable(allocator, &exec_node, case, tmp.dir, bin_name);
- return; // Pass test.
- },
-
- .qemu => |qemu_bin_name| if (enable_qemu) {
- // TODO Ability for test cases to specify whether to link libc.
- const need_cross_glibc = false; // target.isGnuLibC() and self.is_linking_libc;
- const glibc_dir_arg = if (need_cross_glibc)
- glibc_multi_install_dir orelse return // glibc dir not available; pass test
- else
- null;
- try argv.append(qemu_bin_name);
- if (glibc_dir_arg) |dir| {
- const linux_triple = try target.linuxTriple(arena);
- const full_dir = try std.fs.path.join(arena, &[_][]const u8{
- dir,
- linux_triple,
- });
-
- try argv.append("-L");
- try argv.append(full_dir);
- }
- try argv.append(exe_path);
- } else {
- return; // QEMU not available; pass test.
- },
-
- .wine => |wine_bin_name| if (enable_wine) {
- try argv.append(wine_bin_name);
- try argv.append(exe_path);
- } else {
- return; // Wine not available; pass test.
- },
-
- .wasmtime => |wasmtime_bin_name| if (enable_wasmtime) {
- try argv.append(wasmtime_bin_name);
- try argv.append("--dir=.");
- try argv.append(exe_path);
- } else {
- return; // wasmtime not available; pass test.
- },
- }
-
- try module.makeBinFileExecutable();
-
- break :x try std.ChildProcess.exec(.{
- .allocator = allocator,
- .argv = argv.items,
- .cwd_dir = tmp.dir,
- });
- };
- var test_node = update_node.start("test", null);
- test_node.activate();
- defer test_node.end();
- defer allocator.free(exec_result.stdout);
- defer allocator.free(exec_result.stderr);
- switch (exec_result.term) {
- .Exited => |code| {
- if (code != 0) {
- std.debug.print("elf file exited with code {}\n", .{code});
- return error.BinaryBadExitCode;
- }
- },
- else => return error.BinaryCrashed,
- }
- if (!std.mem.eql(u8, expected_stdout, exec_result.stdout)) {
- std.debug.panic(
- "update index {}, mismatched stdout\n====Expected (len={}):====\n{}\n====Actual (len={}):====\n{}\n========\n",
- .{ update_index, expected_stdout.len, expected_stdout, exec_result.stdout.len, exec_result.stdout },
- );
- }
- },
- }
- }
- }
-
- fn runInterpreterIfAvailable(
- self: *TestContext,
- gpa: *Allocator,
- node: *std.Progress.Node,
- case: Case,
- tmp_dir: std.fs.Dir,
- bin_name: []const u8,
- ) !void {
- const arch = case.target.cpu_arch orelse return;
- switch (arch) {
- .spu_2 => return self.runSpu2Interpreter(gpa, node, case, tmp_dir, bin_name),
- else => return,
- }
- }
-
- fn runSpu2Interpreter(
- self: *TestContext,
- gpa: *Allocator,
- update_node: *std.Progress.Node,
- case: Case,
- tmp_dir: std.fs.Dir,
- bin_name: []const u8,
- ) !void {
- const spu = @import("codegen/spu-mk2.zig");
- if (case.target.os_tag) |os| {
- if (os != .freestanding) {
- std.debug.panic("Only freestanding makes sense for SPU-II tests!", .{});
- }
- } else {
- std.debug.panic("SPU_2 has no native OS, check the test!", .{});
- }
-
- var interpreter = spu.Interpreter(struct {
- 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 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);
- }
- }){
- .bus = .{},
- };
-
- {
- var load_node = update_node.start("load", null);
- load_node.activate();
- defer load_node.end();
-
- var file = try tmp_dir.openFile(bin_name, .{ .read = true });
- defer file.close();
-
- const header = try std.elf.readHeader(file);
- var iterator = header.program_header_iterator(file);
-
- var none_loaded = true;
-
- while (try iterator.next()) |phdr| {
- if (phdr.p_type != std.elf.PT_LOAD) {
- std.debug.print("Encountered unexpected ELF program header: type {}\n", .{phdr.p_type});
- std.process.exit(1);
- }
- if (phdr.p_paddr != phdr.p_vaddr) {
- std.debug.print("Physical address does not match virtual address in ELF header!\n", .{});
- std.process.exit(1);
- }
- if (phdr.p_filesz != phdr.p_memsz) {
- std.debug.print("Physical size does not match virtual size in ELF header!\n", .{});
- std.process.exit(1);
- }
- if ((try file.pread(interpreter.bus.RAM[phdr.p_paddr .. phdr.p_paddr + phdr.p_filesz], phdr.p_offset)) != phdr.p_filesz) {
- std.debug.print("Read less than expected from ELF file!", .{});
- std.process.exit(1);
- }
- std.log.scoped(.spu2_test).debug("Loaded 0x{x} bytes to 0x{x:0<4}\n", .{ phdr.p_filesz, phdr.p_paddr });
- none_loaded = false;
- }
- if (none_loaded) {
- std.debug.print("No data found in ELF file!\n", .{});
- std.process.exit(1);
- }
- }
-
- var exec_node = update_node.start("execute", null);
- exec_node.activate();
- defer exec_node.end();
-
- var blocks: u16 = 1000;
- const block_size = 1000;
- while (!interpreter.undefined0) {
- const pre_ip = interpreter.ip;
- if (blocks > 0) {
- blocks -= 1;
- try interpreter.ExecuteBlock(block_size);
- if (pre_ip == interpreter.ip) {
- std.debug.print("Infinite loop detected in SPU II test!\n", .{});
- std.process.exit(1);
- }
- }
- }
- }
-};
diff --git a/src-self-hosted/tracy.zig b/src-self-hosted/tracy.zig
deleted file mode 100644
index 6f56a87ce6fad8484cfc8f37ff5e4e97ca0570f1..0000000000000000000000000000000000000000
--- a/src-self-hosted/tracy.zig
+++ /dev/null
@@ -1,45 +0,0 @@
-pub const std = @import("std");
-
-pub const enable = if (std.builtin.is_test) false else @import("build_options").enable_tracy;
-
-extern fn ___tracy_emit_zone_begin_callstack(
- srcloc: *const ___tracy_source_location_data,
- depth: c_int,
- active: c_int,
-) ___tracy_c_zone_context;
-
-extern fn ___tracy_emit_zone_end(ctx: ___tracy_c_zone_context) void;
-
-pub const ___tracy_source_location_data = extern struct {
- name: ?[*:0]const u8,
- function: [*:0]const u8,
- file: [*:0]const u8,
- line: u32,
- color: u32,
-};
-
-pub const ___tracy_c_zone_context = extern struct {
- id: u32,
- active: c_int,
-
- pub fn end(self: ___tracy_c_zone_context) void {
- ___tracy_emit_zone_end(self);
- }
-};
-
-pub const Ctx = if (enable) ___tracy_c_zone_context else struct {
- pub fn end(self: Ctx) void {}
-};
-
-pub inline fn trace(comptime src: std.builtin.SourceLocation) Ctx {
- if (!enable) return .{};
-
- const loc: ___tracy_source_location_data = .{
- .name = null,
- .function = src.fn_name.ptr,
- .file = src.file.ptr,
- .line = src.line,
- .color = 0,
- };
- return ___tracy_emit_zone_begin_callstack(&loc, 1, 1);
-}
diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig
deleted file mode 100644
index a5619d56fe7c8cefd922ebbd2f4380ba396361b2..0000000000000000000000000000000000000000
--- a/src-self-hosted/translate_c.zig
+++ /dev/null
@@ -1,6470 +0,0 @@
-// 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;
-const ast = std.zig.ast;
-const Token = std.zig.Token;
-usingnamespace @import("clang.zig");
-const ctok = std.c.tokenizer;
-const CToken = std.c.Token;
-const mem = std.mem;
-const math = std.math;
-
-const CallingConvention = std.builtin.CallingConvention;
-
-pub const ClangErrMsg = Stage2ErrorMsg;
-
-pub const Error = error{OutOfMemory};
-const TypeError = Error || error{UnsupportedType};
-const TransError = TypeError || error{UnsupportedTranslation};
-
-const DeclTable = std.AutoArrayHashMap(usize, []const u8);
-
-const SymbolTable = std.StringArrayHashMap(*ast.Node);
-const AliasList = std.ArrayList(struct {
- alias: []const u8,
- name: []const u8,
-});
-
-const Scope = struct {
- id: Id,
- parent: ?*Scope,
-
- const Id = enum {
- Switch,
- Block,
- Root,
- Condition,
- Loop,
- };
-
- /// Represents an in-progress ast.Node.Switch. This struct is stack-allocated.
- /// When it is deinitialized, it produces an ast.Node.Switch which is allocated
- /// into the main arena.
- const Switch = struct {
- base: Scope,
- pending_block: Block,
- cases: []*ast.Node,
- case_index: usize,
- switch_label: ?[]const u8,
- default_label: ?[]const u8,
- };
-
- /// Used for the scope of condition expressions, for example `if (cond)`.
- /// The block is lazily initialised because it is only needed for rare
- /// cases of comma operators being used.
- const Condition = struct {
- base: Scope,
- block: ?Block = null,
-
- fn getBlockScope(self: *Condition, c: *Context) !*Block {
- if (self.block) |*b| return b;
- self.block = try Block.init(c, &self.base, true);
- return &self.block.?;
- }
-
- fn deinit(self: *Condition) void {
- if (self.block) |*b| b.deinit();
- }
- };
-
- /// Represents an in-progress ast.Node.Block. This struct is stack-allocated.
- /// When it is deinitialized, it produces an ast.Node.Block which is allocated
- /// into the main arena.
- const Block = struct {
- base: Scope,
- statements: std.ArrayList(*ast.Node),
- variables: AliasList,
- label: ?ast.TokenIndex,
- mangle_count: u32 = 0,
- lbrace: ast.TokenIndex,
-
- fn init(c: *Context, parent: *Scope, labeled: bool) !Block {
- var blk = Block{
- .base = .{
- .id = .Block,
- .parent = parent,
- },
- .statements = std.ArrayList(*ast.Node).init(c.gpa),
- .variables = AliasList.init(c.gpa),
- .label = null,
- .lbrace = try appendToken(c, .LBrace, "{"),
- };
- if (labeled) {
- blk.label = try appendIdentifier(c, try blk.makeMangledName(c, "blk"));
- _ = try appendToken(c, .Colon, ":");
- }
- return blk;
- }
-
- fn deinit(self: *Block) void {
- self.statements.deinit();
- self.variables.deinit();
- self.* = undefined;
- }
-
- fn complete(self: *Block, c: *Context) !*ast.Node {
- // We reserve 1 extra statement if the parent is a Loop. This is in case of
- // do while, we want to put `if (cond) break;` at the end.
- const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .Loop);
- const rbrace = try appendToken(c, .RBrace, "}");
- if (self.label) |label| {
- const node = try ast.Node.LabeledBlock.alloc(c.arena, alloc_len);
- node.* = .{
- .statements_len = self.statements.items.len,
- .lbrace = self.lbrace,
- .rbrace = rbrace,
- .label = label,
- };
- mem.copy(*ast.Node, node.statements(), self.statements.items);
- return &node.base;
- } else {
- const node = try ast.Node.Block.alloc(c.arena, alloc_len);
- node.* = .{
- .statements_len = self.statements.items.len,
- .lbrace = self.lbrace,
- .rbrace = rbrace,
- };
- mem.copy(*ast.Node, node.statements(), self.statements.items);
- return &node.base;
- }
- }
-
- /// Given the desired name, return a name that does not shadow anything from outer scopes.
- /// Inserts the returned name into the scope.
- fn makeMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 {
- const name_copy = try c.arena.dupe(u8, name);
- var proposed_name = name_copy;
- while (scope.contains(proposed_name)) {
- scope.mangle_count += 1;
- proposed_name = try std.fmt.allocPrint(c.arena, "{}_{}", .{ name, scope.mangle_count });
- }
- try scope.variables.append(.{ .name = name_copy, .alias = proposed_name });
- return proposed_name;
- }
-
- fn getAlias(scope: *Block, name: []const u8) []const u8 {
- for (scope.variables.items) |p| {
- if (mem.eql(u8, p.name, name))
- return p.alias;
- }
- return scope.base.parent.?.getAlias(name);
- }
-
- fn localContains(scope: *Block, name: []const u8) bool {
- for (scope.variables.items) |p| {
- if (mem.eql(u8, p.alias, name))
- return true;
- }
- return false;
- }
-
- fn contains(scope: *Block, name: []const u8) bool {
- if (scope.localContains(name))
- return true;
- return scope.base.parent.?.contains(name);
- }
- };
-
- const Root = struct {
- base: Scope,
- sym_table: SymbolTable,
- macro_table: SymbolTable,
- context: *Context,
-
- fn init(c: *Context) Root {
- return .{
- .base = .{
- .id = .Root,
- .parent = null,
- },
- .sym_table = SymbolTable.init(c.arena),
- .macro_table = SymbolTable.init(c.arena),
- .context = c,
- };
- }
-
- /// Check if the global scope contains this name, without looking into the "future", e.g.
- /// ignore the preprocessed decl and macro names.
- fn containsNow(scope: *Root, name: []const u8) bool {
- return isZigPrimitiveType(name) or
- scope.sym_table.contains(name) or
- scope.macro_table.contains(name);
- }
-
- /// Check if the global scope contains the name, includes all decls that haven't been translated yet.
- fn contains(scope: *Root, name: []const u8) bool {
- return scope.containsNow(name) or scope.context.global_names.contains(name);
- }
- };
-
- fn findBlockScope(inner: *Scope, c: *Context) !*Scope.Block {
- var scope = inner;
- while (true) {
- switch (scope.id) {
- .Root => unreachable,
- .Block => return @fieldParentPtr(Block, "base", scope),
- .Condition => return @fieldParentPtr(Condition, "base", scope).getBlockScope(c),
- else => scope = scope.parent.?,
- }
- }
- }
-
- fn getAlias(scope: *Scope, name: []const u8) []const u8 {
- return switch (scope.id) {
- .Root => return name,
- .Block => @fieldParentPtr(Block, "base", scope).getAlias(name),
- .Switch, .Loop, .Condition => scope.parent.?.getAlias(name),
- };
- }
-
- fn contains(scope: *Scope, name: []const u8) bool {
- return switch (scope.id) {
- .Root => @fieldParentPtr(Root, "base", scope).contains(name),
- .Block => @fieldParentPtr(Block, "base", scope).contains(name),
- .Switch, .Loop, .Condition => scope.parent.?.contains(name),
- };
- }
-
- fn getBreakableScope(inner: *Scope) *Scope {
- var scope = inner;
- while (true) {
- switch (scope.id) {
- .Root => unreachable,
- .Switch => return scope,
- .Loop => return scope,
- else => scope = scope.parent.?,
- }
- }
- }
-
- fn getSwitch(inner: *Scope) *Scope.Switch {
- var scope = inner;
- while (true) {
- switch (scope.id) {
- .Root => unreachable,
- .Switch => return @fieldParentPtr(Switch, "base", scope),
- else => scope = scope.parent.?,
- }
- }
- }
-};
-
-pub const Context = struct {
- gpa: *mem.Allocator,
- arena: *mem.Allocator,
- token_ids: std.ArrayListUnmanaged(Token.Id),
- token_locs: std.ArrayListUnmanaged(Token.Loc),
- errors: std.ArrayListUnmanaged(ast.Error),
- source_buffer: *std.ArrayList(u8),
- err: Error,
- source_manager: *ZigClangSourceManager,
- decl_table: DeclTable,
- alias_list: AliasList,
- global_scope: *Scope.Root,
- clang_context: *ZigClangASTContext,
- mangle_count: u32 = 0,
- root_decls: std.ArrayListUnmanaged(*ast.Node),
-
- /// This one is different than the root scope's name table. This contains
- /// a list of names that we found by visiting all the top level decls without
- /// translating them. The other maps are updated as we translate; this one is updated
- /// up front in a pre-processing step.
- global_names: std.StringArrayHashMap(void),
-
- fn getMangle(c: *Context) u32 {
- c.mangle_count += 1;
- return c.mangle_count;
- }
-
- /// Convert a null-terminated C string to a slice allocated in the arena
- fn str(c: *Context, s: [*:0]const u8) ![]u8 {
- return mem.dupe(c.arena, u8, mem.spanZ(s));
- }
-
- /// Convert a clang source location to a file:line:column string
- fn locStr(c: *Context, loc: ZigClangSourceLocation) ![]u8 {
- const spelling_loc = ZigClangSourceManager_getSpellingLoc(c.source_manager, loc);
- const filename_c = ZigClangSourceManager_getFilename(c.source_manager, spelling_loc);
- const filename = if (filename_c) |s| try c.str(s) else @as([]const u8, "(no file)");
-
- const line = ZigClangSourceManager_getSpellingLineNumber(c.source_manager, spelling_loc);
- const column = ZigClangSourceManager_getSpellingColumnNumber(c.source_manager, spelling_loc);
- return std.fmt.allocPrint(c.arena, "{}:{}:{}", .{ filename, line, column });
- }
-
- fn createCall(c: *Context, fn_expr: *ast.Node, params_len: ast.NodeIndex) !*ast.Node.Call {
- _ = try appendToken(c, .LParen, "(");
- const node = try ast.Node.Call.alloc(c.arena, params_len);
- node.* = .{
- .lhs = fn_expr,
- .params_len = params_len,
- .async_token = null,
- .rtoken = undefined, // set after appending args
- };
- return node;
- }
-
- fn createBuiltinCall(c: *Context, name: []const u8, params_len: ast.NodeIndex) !*ast.Node.BuiltinCall {
- const builtin_token = try appendToken(c, .Builtin, name);
- _ = try appendToken(c, .LParen, "(");
- const node = try ast.Node.BuiltinCall.alloc(c.arena, params_len);
- node.* = .{
- .builtin_token = builtin_token,
- .params_len = params_len,
- .rparen_token = undefined, // set after appending args
- };
- return node;
- }
-
- fn createBlock(c: *Context, statements_len: ast.NodeIndex) !*ast.Node.Block {
- const block_node = try ast.Node.Block.alloc(c.arena, statements_len);
- block_node.* = .{
- .lbrace = try appendToken(c, .LBrace, "{"),
- .statements_len = statements_len,
- .rbrace = undefined,
- };
- return block_node;
- }
-};
-
-pub fn translate(
- gpa: *mem.Allocator,
- args_begin: [*]?[*]const u8,
- args_end: [*]?[*]const u8,
- errors: *[]ClangErrMsg,
- resources_path: [*:0]const u8,
-) !*ast.Tree {
- const ast_unit = ZigClangLoadFromCommandLine(
- args_begin,
- args_end,
- &errors.ptr,
- &errors.len,
- resources_path,
- ) orelse {
- if (errors.len == 0) return error.ASTUnitFailure;
- return error.SemanticAnalyzeFail;
- };
- defer ZigClangASTUnit_delete(ast_unit);
-
- var source_buffer = std.ArrayList(u8).init(gpa);
- defer source_buffer.deinit();
-
- // For memory that has the same lifetime as the Tree that we return
- // from this function.
- var arena = std.heap.ArenaAllocator.init(gpa);
- errdefer arena.deinit();
-
- var context = Context{
- .gpa = gpa,
- .arena = &arena.allocator,
- .source_buffer = &source_buffer,
- .source_manager = ZigClangASTUnit_getSourceManager(ast_unit),
- .err = undefined,
- .decl_table = DeclTable.init(gpa),
- .alias_list = AliasList.init(gpa),
- .global_scope = try arena.allocator.create(Scope.Root),
- .clang_context = ZigClangASTUnit_getASTContext(ast_unit).?,
- .global_names = std.StringArrayHashMap(void).init(gpa),
- .token_ids = .{},
- .token_locs = .{},
- .errors = .{},
- .root_decls = .{},
- };
- context.global_scope.* = Scope.Root.init(&context);
- defer context.decl_table.deinit();
- defer context.alias_list.deinit();
- defer context.token_ids.deinit(gpa);
- defer context.token_locs.deinit(gpa);
- defer context.errors.deinit(gpa);
- defer context.global_names.deinit();
- defer context.root_decls.deinit(gpa);
-
- try prepopulateGlobalNameTable(ast_unit, &context);
-
- if (!ZigClangASTUnit_visitLocalTopLevelDecls(ast_unit, &context, declVisitorC)) {
- return context.err;
- }
-
- try transPreprocessorEntities(&context, ast_unit);
-
- try addMacros(&context);
- for (context.alias_list.items) |alias| {
- if (!context.global_scope.sym_table.contains(alias.alias)) {
- try createAlias(&context, alias);
- }
- }
-
- const eof_token = try appendToken(&context, .Eof, "");
- const root_node = try ast.Node.Root.create(&arena.allocator, context.root_decls.items.len, eof_token);
- mem.copy(*ast.Node, root_node.decls(), context.root_decls.items);
-
- if (false) {
- std.debug.warn("debug source:\n{}\n==EOF==\ntokens:\n", .{source_buffer.items});
- for (context.token_ids.items) |token| {
- std.debug.warn("{}\n", .{token});
- }
- }
-
- const tree = try arena.allocator.create(ast.Tree);
- tree.* = .{
- .gpa = gpa,
- .source = try arena.allocator.dupe(u8, source_buffer.items),
- .token_ids = context.token_ids.toOwnedSlice(gpa),
- .token_locs = context.token_locs.toOwnedSlice(gpa),
- .errors = context.errors.toOwnedSlice(gpa),
- .root_node = root_node,
- .arena = arena.state,
- .generated = true,
- };
- return tree;
-}
-
-fn prepopulateGlobalNameTable(ast_unit: *ZigClangASTUnit, c: *Context) !void {
- if (!ZigClangASTUnit_visitLocalTopLevelDecls(ast_unit, c, declVisitorNamesOnlyC)) {
- return c.err;
- }
-
- // TODO if we see #undef, delete it from the table
- var it = ZigClangASTUnit_getLocalPreprocessingEntities_begin(ast_unit);
- const it_end = ZigClangASTUnit_getLocalPreprocessingEntities_end(ast_unit);
-
- while (it.I != it_end.I) : (it.I += 1) {
- const entity = ZigClangPreprocessingRecord_iterator_deref(it);
- switch (ZigClangPreprocessedEntity_getKind(entity)) {
- .MacroDefinitionKind => {
- const macro = @ptrCast(*ZigClangMacroDefinitionRecord, entity);
- const raw_name = ZigClangMacroDefinitionRecord_getName_getNameStart(macro);
- const name = try c.str(raw_name);
- _ = try c.global_names.put(name, {});
- },
- else => {},
- }
- }
-}
-
-fn declVisitorNamesOnlyC(context: ?*c_void, decl: *const ZigClangDecl) callconv(.C) bool {
- const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context));
- declVisitorNamesOnly(c, decl) catch |err| {
- c.err = err;
- return false;
- };
- return true;
-}
-
-fn declVisitorC(context: ?*c_void, decl: *const ZigClangDecl) callconv(.C) bool {
- const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context));
- declVisitor(c, decl) catch |err| {
- c.err = err;
- return false;
- };
- return true;
-}
-
-fn declVisitorNamesOnly(c: *Context, decl: *const ZigClangDecl) Error!void {
- if (ZigClangDecl_castToNamedDecl(decl)) |named_decl| {
- const decl_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(named_decl));
- _ = try c.global_names.put(decl_name, {});
- }
-}
-
-fn declVisitor(c: *Context, decl: *const ZigClangDecl) Error!void {
- switch (ZigClangDecl_getKind(decl)) {
- .Function => {
- return visitFnDecl(c, @ptrCast(*const ZigClangFunctionDecl, decl));
- },
- .Typedef => {
- _ = try transTypeDef(c, @ptrCast(*const ZigClangTypedefNameDecl, decl), true);
- },
- .Enum => {
- _ = try transEnumDecl(c, @ptrCast(*const ZigClangEnumDecl, decl));
- },
- .Record => {
- _ = try transRecordDecl(c, @ptrCast(*const ZigClangRecordDecl, decl));
- },
- .Var => {
- return visitVarDecl(c, @ptrCast(*const ZigClangVarDecl, decl), null);
- },
- .Empty => {
- // Do nothing
- },
- else => {
- const decl_name = try c.str(ZigClangDecl_getDeclKindName(decl));
- try emitWarning(c, ZigClangDecl_getLocation(decl), "ignoring {} declaration", .{decl_name});
- },
- }
-}
-
-fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
- const fn_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, fn_decl)));
- if (c.global_scope.sym_table.contains(fn_name))
- return; // Avoid processing this decl twice
-
- // Skip this declaration if a proper definition exists
- if (!ZigClangFunctionDecl_isThisDeclarationADefinition(fn_decl)) {
- if (ZigClangFunctionDecl_getDefinition(fn_decl)) |def|
- return visitFnDecl(c, def);
- }
-
- const rp = makeRestorePoint(c);
- const fn_decl_loc = ZigClangFunctionDecl_getLocation(fn_decl);
- const has_body = ZigClangFunctionDecl_hasBody(fn_decl);
- const storage_class = ZigClangFunctionDecl_getStorageClass(fn_decl);
- const decl_ctx = FnDeclContext{
- .fn_name = fn_name,
- .has_body = has_body,
- .storage_class = storage_class,
- .is_export = switch (storage_class) {
- .None => has_body and !ZigClangFunctionDecl_isInlineSpecified(fn_decl),
- .Extern, .Static => false,
- .PrivateExtern => return failDecl(c, fn_decl_loc, fn_name, "unsupported storage class: private extern", .{}),
- .Auto => unreachable, // Not legal on functions
- .Register => unreachable, // Not legal on functions
- },
- };
-
- var fn_qt = ZigClangFunctionDecl_getType(fn_decl);
-
- const fn_type = while (true) {
- const fn_type = ZigClangQualType_getTypePtr(fn_qt);
-
- switch (ZigClangType_getTypeClass(fn_type)) {
- .Attributed => {
- const attr_type = @ptrCast(*const ZigClangAttributedType, fn_type);
- fn_qt = ZigClangAttributedType_getEquivalentType(attr_type);
- },
- .Paren => {
- const paren_type = @ptrCast(*const ZigClangParenType, fn_type);
- fn_qt = ZigClangParenType_getInnerType(paren_type);
- },
- else => break fn_type,
- }
- } else unreachable;
-
- const proto_node = switch (ZigClangType_getTypeClass(fn_type)) {
- .FunctionProto => blk: {
- const fn_proto_type = @ptrCast(*const ZigClangFunctionProtoType, fn_type);
- break :blk transFnProto(rp, fn_decl, fn_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) {
- error.UnsupportedType => {
- return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{});
- },
- error.OutOfMemory => |e| return e,
- };
- },
- .FunctionNoProto => blk: {
- const fn_no_proto_type = @ptrCast(*const ZigClangFunctionType, fn_type);
- break :blk transFnNoProto(rp, fn_no_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) {
- error.UnsupportedType => {
- return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{});
- },
- error.OutOfMemory => |e| return e,
- };
- },
- else => return failDecl(c, fn_decl_loc, fn_name, "unable to resolve function type {}", .{ZigClangType_getTypeClass(fn_type)}),
- };
-
- if (!decl_ctx.has_body) {
- const semi_tok = try appendToken(c, .Semicolon, ";");
- return addTopLevelDecl(c, fn_name, &proto_node.base);
- }
-
- // actual function definition with body
- const body_stmt = ZigClangFunctionDecl_getBody(fn_decl);
- var block_scope = try Scope.Block.init(rp.c, &c.global_scope.base, false);
- defer block_scope.deinit();
- var scope = &block_scope.base;
-
- var param_id: c_uint = 0;
- for (proto_node.params()) |*param, i| {
- const param_name = if (param.name_token) |name_tok|
- tokenSlice(c, name_tok)
- else
- return failDecl(c, fn_decl_loc, fn_name, "function {} parameter has no name", .{fn_name});
-
- const c_param = ZigClangFunctionDecl_getParamDecl(fn_decl, param_id);
- const qual_type = ZigClangParmVarDecl_getOriginalType(c_param);
- const is_const = ZigClangQualType_isConstQualified(qual_type);
-
- const mangled_param_name = try block_scope.makeMangledName(c, param_name);
-
- if (!is_const) {
- const bare_arg_name = try std.fmt.allocPrint(c.arena, "arg_{}", .{mangled_param_name});
- const arg_name = try block_scope.makeMangledName(c, bare_arg_name);
-
- const mut_tok = try appendToken(c, .Keyword_var, "var");
- const name_tok = try appendIdentifier(c, mangled_param_name);
- const eq_token = try appendToken(c, .Equal, "=");
- const init_node = try transCreateNodeIdentifier(c, arg_name);
- const semicolon_token = try appendToken(c, .Semicolon, ";");
- const node = try ast.Node.VarDecl.create(c.arena, .{
- .mut_token = mut_tok,
- .name_token = name_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .eq_token = eq_token,
- .init_node = init_node,
- });
- try block_scope.statements.append(&node.base);
- param.name_token = try appendIdentifier(c, arg_name);
- _ = try appendToken(c, .Colon, ":");
- }
-
- param_id += 1;
- }
-
- const casted_body = @ptrCast(*const ZigClangCompoundStmt, body_stmt);
- transCompoundStmtInline(rp, &block_scope.base, casted_body, &block_scope) catch |err| switch (err) {
- error.OutOfMemory => |e| return e,
- error.UnsupportedTranslation,
- error.UnsupportedType,
- => return failDecl(c, fn_decl_loc, fn_name, "unable to translate function", .{}),
- };
- // add return statement if the function didn't have one
- blk: {
- const fn_ty = @ptrCast(*const ZigClangFunctionType, fn_type);
-
- if (ZigClangFunctionType_getNoReturnAttr(fn_ty)) break :blk;
- const return_qt = ZigClangFunctionType_getReturnType(fn_ty);
- if (isCVoid(return_qt)) break :blk;
-
- if (block_scope.statements.items.len > 0) {
- var last = block_scope.statements.items[block_scope.statements.items.len - 1];
- while (true) {
- switch (last.tag) {
- .Block, .LabeledBlock => {
- const stmts = last.blockStatements();
- if (stmts.len == 0) break;
-
- last = stmts[stmts.len - 1];
- },
- // no extra return needed
- .Return => break :blk,
- else => break,
- }
- }
- }
-
- const return_expr = try ast.Node.ControlFlowExpression.create(rp.c.arena, .{
- .ltoken = try appendToken(rp.c, .Keyword_return, "return"),
- .tag = .Return,
- }, .{
- .rhs = transZeroInitExpr(rp, scope, fn_decl_loc, ZigClangQualType_getTypePtr(return_qt)) catch |err| switch (err) {
- error.OutOfMemory => |e| return e,
- error.UnsupportedTranslation,
- error.UnsupportedType,
- => return failDecl(c, fn_decl_loc, fn_name, "unable to create a return value for function", .{}),
- },
- });
- _ = try appendToken(rp.c, .Semicolon, ";");
- try block_scope.statements.append(&return_expr.base);
- }
-
- const body_node = try block_scope.complete(rp.c);
- proto_node.setBodyNode(body_node);
- return addTopLevelDecl(c, fn_name, &proto_node.base);
-}
-
-/// if mangled_name is not null, this var decl was declared in a block scope.
-fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl, mangled_name: ?[]const u8) Error!void {
- const var_name = mangled_name orelse try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, var_decl)));
- if (c.global_scope.sym_table.contains(var_name))
- return; // Avoid processing this decl twice
- const rp = makeRestorePoint(c);
- const visib_tok = if (mangled_name) |_| null else try appendToken(c, .Keyword_pub, "pub");
-
- const thread_local_token = if (ZigClangVarDecl_getTLSKind(var_decl) == .None)
- null
- else
- try appendToken(c, .Keyword_threadlocal, "threadlocal");
-
- const scope = &c.global_scope.base;
-
- // TODO https://github.com/ziglang/zig/issues/3756
- // TODO https://github.com/ziglang/zig/issues/1802
- const checked_name = if (isZigPrimitiveType(var_name)) try std.fmt.allocPrint(c.arena, "{}_{}", .{ var_name, c.getMangle() }) else var_name;
- const var_decl_loc = ZigClangVarDecl_getLocation(var_decl);
-
- const qual_type = ZigClangVarDecl_getTypeSourceInfo_getType(var_decl);
- const storage_class = ZigClangVarDecl_getStorageClass(var_decl);
- const is_const = ZigClangQualType_isConstQualified(qual_type);
- const has_init = ZigClangVarDecl_hasInit(var_decl);
-
- // In C extern variables with initializers behave like Zig exports.
- // extern int foo = 2;
- // does the same as:
- // extern int foo;
- // int foo = 2;
- const extern_tok = if (storage_class == .Extern and !has_init)
- try appendToken(c, .Keyword_extern, "extern")
- else if (storage_class != .Static)
- try appendToken(c, .Keyword_export, "export")
- else
- null;
-
- const mut_tok = if (is_const)
- try appendToken(c, .Keyword_const, "const")
- else
- try appendToken(c, .Keyword_var, "var");
-
- const name_tok = try appendIdentifier(c, checked_name);
-
- _ = try appendToken(c, .Colon, ":");
- const type_node = transQualType(rp, qual_type, var_decl_loc) catch |err| switch (err) {
- error.UnsupportedType => {
- return failDecl(c, var_decl_loc, checked_name, "unable to resolve variable type", .{});
- },
- error.OutOfMemory => |e| return e,
- };
-
- var eq_tok: ast.TokenIndex = undefined;
- var init_node: ?*ast.Node = null;
-
- // If the initialization expression is not present, initialize with undefined.
- // If it is an integer literal, we can skip the @as since it will be redundant
- // with the variable type.
- if (has_init) {
- eq_tok = try appendToken(c, .Equal, "=");
- init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr|
- transExprCoercing(rp, &c.global_scope.base, expr, .used, .r_value) catch |err| switch (err) {
- error.UnsupportedTranslation,
- error.UnsupportedType,
- => {
- return failDecl(c, var_decl_loc, checked_name, "unable to translate initializer", .{});
- },
- error.OutOfMemory => |e| return e,
- }
- else
- try transCreateNodeUndefinedLiteral(c);
- } else if (storage_class != .Extern) {
- eq_tok = try appendToken(c, .Equal, "=");
- // The C language specification states that variables with static or threadlocal
- // storage without an initializer are initialized to a zero value.
-
- // @import("std").mem.zeroes(T)
- const import_fn_call = try c.createBuiltinCall("@import", 1);
- const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
- import_fn_call.params()[0] = std_node;
- import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
- const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "mem");
- const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "zeroes");
-
- const zero_init_call = try c.createCall(outer_field_access, 1);
- zero_init_call.params()[0] = type_node;
- zero_init_call.rtoken = try appendToken(c, .RParen, ")");
-
- init_node = &zero_init_call.base;
- }
-
- const linksection_expr = blk: {
- var str_len: usize = undefined;
- if (ZigClangVarDecl_getSectionAttribute(var_decl, &str_len)) |str_ptr| {
- _ = try appendToken(rp.c, .Keyword_linksection, "linksection");
- _ = try appendToken(rp.c, .LParen, "(");
- const expr = try transCreateNodeStringLiteral(
- rp.c,
- try std.fmt.allocPrint(rp.c.arena, "\"{}\"", .{str_ptr[0..str_len]}),
- );
- _ = try appendToken(rp.c, .RParen, ")");
-
- break :blk expr;
- }
- break :blk null;
- };
-
- const align_expr = blk: {
- const alignment = ZigClangVarDecl_getAlignedAttribute(var_decl, rp.c.clang_context);
- if (alignment != 0) {
- _ = try appendToken(rp.c, .Keyword_align, "align");
- _ = try appendToken(rp.c, .LParen, "(");
- // Clang reports the alignment in bits
- const expr = try transCreateNodeInt(rp.c, alignment / 8);
- _ = try appendToken(rp.c, .RParen, ")");
-
- break :blk expr;
- }
- break :blk null;
- };
-
- const node = try ast.Node.VarDecl.create(c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = try appendToken(c, .Semicolon, ";"),
- }, .{
- .visib_token = visib_tok,
- .thread_local_token = thread_local_token,
- .eq_token = eq_tok,
- .extern_export_token = extern_tok,
- .type_node = type_node,
- .align_node = align_expr,
- .section_node = linksection_expr,
- .init_node = init_node,
- });
- return addTopLevelDecl(c, checked_name, &node.base);
-}
-
-fn transTypeDefAsBuiltin(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl, builtin_name: []const u8) !*ast.Node {
- _ = try c.decl_table.put(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)), builtin_name);
- return transCreateNodeIdentifier(c, builtin_name);
-}
-
-fn checkForBuiltinTypedef(checked_name: []const u8) ?[]const u8 {
- const table = [_][2][]const u8{
- .{ "uint8_t", "u8" },
- .{ "int8_t", "i8" },
- .{ "uint16_t", "u16" },
- .{ "int16_t", "i16" },
- .{ "uint32_t", "u32" },
- .{ "int32_t", "i32" },
- .{ "uint64_t", "u64" },
- .{ "int64_t", "i64" },
- .{ "intptr_t", "isize" },
- .{ "uintptr_t", "usize" },
- .{ "ssize_t", "isize" },
- .{ "size_t", "usize" },
- };
-
- for (table) |entry| {
- if (mem.eql(u8, checked_name, entry[0])) {
- return entry[1];
- }
- }
-
- return null;
-}
-
-fn transTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl, top_level_visit: bool) Error!?*ast.Node {
- if (c.decl_table.get(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)))) |name|
- return transCreateNodeIdentifier(c, name); // Avoid processing this decl twice
- const rp = makeRestorePoint(c);
-
- const typedef_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, typedef_decl)));
-
- // TODO https://github.com/ziglang/zig/issues/3756
- // TODO https://github.com/ziglang/zig/issues/1802
- const checked_name = if (isZigPrimitiveType(typedef_name)) try std.fmt.allocPrint(c.arena, "{}_{}", .{ typedef_name, c.getMangle() }) else typedef_name;
- if (checkForBuiltinTypedef(checked_name)) |builtin| {
- return transTypeDefAsBuiltin(c, typedef_decl, builtin);
- }
-
- if (!top_level_visit) {
- return transCreateNodeIdentifier(c, checked_name);
- }
-
- _ = try c.decl_table.put(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)), checked_name);
- const node = (try transCreateNodeTypedef(rp, typedef_decl, true, checked_name)) orelse return null;
- try addTopLevelDecl(c, checked_name, node);
- return transCreateNodeIdentifier(c, checked_name);
-}
-
-fn transCreateNodeTypedef(
- rp: RestorePoint,
- typedef_decl: *const ZigClangTypedefNameDecl,
- toplevel: bool,
- checked_name: []const u8,
-) Error!?*ast.Node {
- const visib_tok = if (toplevel) try appendToken(rp.c, .Keyword_pub, "pub") else null;
- const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(rp.c, checked_name);
- const eq_token = try appendToken(rp.c, .Equal, "=");
- const child_qt = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl);
- const typedef_loc = ZigClangTypedefNameDecl_getLocation(typedef_decl);
- const init_node = transQualType(rp, child_qt, typedef_loc) catch |err| switch (err) {
- error.UnsupportedType => {
- try failDecl(rp.c, typedef_loc, checked_name, "unable to resolve typedef child type", .{});
- return null;
- },
- error.OutOfMemory => |e| return e,
- };
- const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
-
- const node = try ast.Node.VarDecl.create(rp.c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .visib_token = visib_tok,
- .eq_token = eq_token,
- .init_node = init_node,
- });
- return &node.base;
-}
-
-fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*ast.Node {
- if (c.decl_table.get(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)))) |name|
- return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice
- const record_loc = ZigClangRecordDecl_getLocation(record_decl);
-
- var bare_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, record_decl)));
- var is_unnamed = false;
- // Record declarations such as `struct {...} x` have no name but they're not
- // anonymous hence here isAnonymousStructOrUnion is not needed
- if (bare_name.len == 0) {
- bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{}", .{c.getMangle()});
- is_unnamed = true;
- }
-
- var container_kind_name: []const u8 = undefined;
- var container_kind: std.zig.Token.Id = undefined;
- if (ZigClangRecordDecl_isUnion(record_decl)) {
- container_kind_name = "union";
- container_kind = .Keyword_union;
- } else if (ZigClangRecordDecl_isStruct(record_decl)) {
- container_kind_name = "struct";
- container_kind = .Keyword_struct;
- } else {
- try emitWarning(c, record_loc, "record {} is not a struct or union", .{bare_name});
- return null;
- }
-
- const name = try std.fmt.allocPrint(c.arena, "{}_{}", .{ container_kind_name, bare_name });
- _ = try c.decl_table.put(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)), name);
-
- const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null;
- const mut_tok = try appendToken(c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(c, name);
-
- const eq_token = try appendToken(c, .Equal, "=");
-
- var semicolon: ast.TokenIndex = undefined;
- const init_node = blk: {
- const rp = makeRestorePoint(c);
- const record_def = ZigClangRecordDecl_getDefinition(record_decl) orelse {
- const opaque = try transCreateNodeOpaqueType(c);
- semicolon = try appendToken(c, .Semicolon, ";");
- break :blk opaque;
- };
-
- const layout_tok = try if (ZigClangRecordDecl_getPackedAttribute(record_decl))
- appendToken(c, .Keyword_packed, "packed")
- else
- appendToken(c, .Keyword_extern, "extern");
- const container_tok = try appendToken(c, container_kind, container_kind_name);
- const lbrace_token = try appendToken(c, .LBrace, "{");
-
- var fields_and_decls = std.ArrayList(*ast.Node).init(c.gpa);
- defer fields_and_decls.deinit();
-
- var unnamed_field_count: u32 = 0;
- var it = ZigClangRecordDecl_field_begin(record_def);
- const end_it = ZigClangRecordDecl_field_end(record_def);
- while (ZigClangRecordDecl_field_iterator_neq(it, end_it)) : (it = ZigClangRecordDecl_field_iterator_next(it)) {
- const field_decl = ZigClangRecordDecl_field_iterator_deref(it);
- const field_loc = ZigClangFieldDecl_getLocation(field_decl);
- const field_qt = ZigClangFieldDecl_getType(field_decl);
-
- if (ZigClangFieldDecl_isBitField(field_decl)) {
- const opaque = try transCreateNodeOpaqueType(c);
- semicolon = try appendToken(c, .Semicolon, ";");
- try emitWarning(c, field_loc, "{} demoted to opaque type - has bitfield", .{container_kind_name});
- break :blk opaque;
- }
-
- if (ZigClangType_isIncompleteOrZeroLengthArrayType(qualTypeCanon(field_qt), c.clang_context)) {
- const opaque = try transCreateNodeOpaqueType(c);
- semicolon = try appendToken(c, .Semicolon, ";");
- try emitWarning(c, field_loc, "{} demoted to opaque type - has variable length array", .{container_kind_name});
- break :blk opaque;
- }
-
- var is_anon = false;
- var raw_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, field_decl)));
- if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl) or raw_name.len == 0) {
- // Context.getMangle() is not used here because doing so causes unpredictable field names for anonymous fields.
- raw_name = try std.fmt.allocPrint(c.arena, "unnamed_{}", .{unnamed_field_count});
- unnamed_field_count += 1;
- is_anon = true;
- }
- const field_name = try appendIdentifier(c, raw_name);
- _ = try appendToken(c, .Colon, ":");
- const field_type = transQualType(rp, field_qt, field_loc) catch |err| switch (err) {
- error.UnsupportedType => {
- const opaque = try transCreateNodeOpaqueType(c);
- semicolon = try appendToken(c, .Semicolon, ";");
- try emitWarning(c, record_loc, "{} demoted to opaque type - unable to translate type of field {}", .{ container_kind_name, raw_name });
- break :blk opaque;
- },
- else => |e| return e,
- };
-
- const align_expr = blk_2: {
- const alignment = ZigClangFieldDecl_getAlignedAttribute(field_decl, rp.c.clang_context);
- if (alignment != 0) {
- _ = try appendToken(rp.c, .Keyword_align, "align");
- _ = try appendToken(rp.c, .LParen, "(");
- // Clang reports the alignment in bits
- const expr = try transCreateNodeInt(rp.c, alignment / 8);
- _ = try appendToken(rp.c, .RParen, ")");
-
- break :blk_2 expr;
- }
- break :blk_2 null;
- };
-
- const field_node = try c.arena.create(ast.Node.ContainerField);
- field_node.* = .{
- .doc_comments = null,
- .comptime_token = null,
- .name_token = field_name,
- .type_expr = field_type,
- .value_expr = null,
- .align_expr = align_expr,
- };
-
- if (is_anon) {
- _ = try c.decl_table.put(
- @ptrToInt(ZigClangFieldDecl_getCanonicalDecl(field_decl)),
- raw_name,
- );
- }
-
- try fields_and_decls.append(&field_node.base);
- _ = try appendToken(c, .Comma, ",");
- }
- const container_node = try ast.Node.ContainerDecl.alloc(c.arena, fields_and_decls.items.len);
- container_node.* = .{
- .layout_token = layout_tok,
- .kind_token = container_tok,
- .init_arg_expr = .None,
- .fields_and_decls_len = fields_and_decls.items.len,
- .lbrace_token = lbrace_token,
- .rbrace_token = try appendToken(c, .RBrace, "}"),
- };
- mem.copy(*ast.Node, container_node.fieldsAndDecls(), fields_and_decls.items);
- semicolon = try appendToken(c, .Semicolon, ";");
- break :blk &container_node.base;
- };
-
- const node = try ast.Node.VarDecl.create(c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon,
- }, .{
- .visib_token = visib_tok,
- .eq_token = eq_token,
- .init_node = init_node,
- });
-
- try addTopLevelDecl(c, name, &node.base);
- if (!is_unnamed)
- try c.alias_list.append(.{ .alias = bare_name, .name = name });
- return transCreateNodeIdentifier(c, name);
-}
-
-fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.Node {
- if (c.decl_table.get(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)))) |name|
- return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice
- const rp = makeRestorePoint(c);
- const enum_loc = ZigClangEnumDecl_getLocation(enum_decl);
-
- var bare_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, enum_decl)));
- var is_unnamed = false;
- if (bare_name.len == 0) {
- bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{}", .{c.getMangle()});
- is_unnamed = true;
- }
-
- const name = try std.fmt.allocPrint(c.arena, "enum_{}", .{bare_name});
- _ = try c.decl_table.put(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)), name);
-
- const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null;
- const mut_tok = try appendToken(c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(c, name);
- const eq_token = try appendToken(c, .Equal, "=");
-
- const init_node = if (ZigClangEnumDecl_getDefinition(enum_decl)) |enum_def| blk: {
- var pure_enum = true;
- var it = ZigClangEnumDecl_enumerator_begin(enum_def);
- var end_it = ZigClangEnumDecl_enumerator_end(enum_def);
- while (ZigClangEnumDecl_enumerator_iterator_neq(it, end_it)) : (it = ZigClangEnumDecl_enumerator_iterator_next(it)) {
- const enum_const = ZigClangEnumDecl_enumerator_iterator_deref(it);
- if (ZigClangEnumConstantDecl_getInitExpr(enum_const)) |_| {
- pure_enum = false;
- break;
- }
- }
-
- const extern_tok = try appendToken(c, .Keyword_extern, "extern");
- const container_tok = try appendToken(c, .Keyword_enum, "enum");
-
- var fields_and_decls = std.ArrayList(*ast.Node).init(c.gpa);
- defer fields_and_decls.deinit();
-
- const int_type = ZigClangEnumDecl_getIntegerType(enum_decl);
- // The underlying type may be null in case of forward-declared enum
- // types, while that's not ISO-C compliant many compilers allow this and
- // default to the usual integer type used for all the enums.
-
- // default to c_int since msvc and gcc default to different types
- _ = try appendToken(c, .LParen, "(");
- const init_arg_expr = ast.Node.ContainerDecl.InitArg{
- .Type = if (int_type.ptr != null and
- !isCBuiltinType(int_type, .UInt) and
- !isCBuiltinType(int_type, .Int))
- transQualType(rp, int_type, enum_loc) catch |err| switch (err) {
- error.UnsupportedType => {
- try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{});
- return null;
- },
- else => |e| return e,
- }
- else
- try transCreateNodeIdentifier(c, "c_int"),
- };
- _ = try appendToken(c, .RParen, ")");
-
- const lbrace_token = try appendToken(c, .LBrace, "{");
-
- it = ZigClangEnumDecl_enumerator_begin(enum_def);
- end_it = ZigClangEnumDecl_enumerator_end(enum_def);
- while (ZigClangEnumDecl_enumerator_iterator_neq(it, end_it)) : (it = ZigClangEnumDecl_enumerator_iterator_next(it)) {
- const enum_const = ZigClangEnumDecl_enumerator_iterator_deref(it);
-
- const enum_val_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, enum_const)));
-
- const field_name = if (!is_unnamed and mem.startsWith(u8, enum_val_name, bare_name))
- enum_val_name[bare_name.len..]
- else
- enum_val_name;
-
- const field_name_tok = try appendIdentifier(c, field_name);
-
- const int_node = if (!pure_enum) blk_2: {
- _ = try appendToken(c, .Colon, "=");
- break :blk_2 try transCreateNodeAPInt(c, ZigClangEnumConstantDecl_getInitVal(enum_const));
- } else
- null;
-
- const field_node = try c.arena.create(ast.Node.ContainerField);
- field_node.* = .{
- .doc_comments = null,
- .comptime_token = null,
- .name_token = field_name_tok,
- .type_expr = null,
- .value_expr = int_node,
- .align_expr = null,
- };
-
- try fields_and_decls.append(&field_node.base);
- _ = try appendToken(c, .Comma, ",");
-
- // In C each enum value is in the global namespace. So we put them there too.
- // At this point we can rely on the enum emitting successfully.
- const tld_visib_tok = try appendToken(c, .Keyword_pub, "pub");
- const tld_mut_tok = try appendToken(c, .Keyword_const, "const");
- const tld_name_tok = try appendIdentifier(c, enum_val_name);
- const tld_eq_token = try appendToken(c, .Equal, "=");
- const cast_node = try rp.c.createBuiltinCall("@enumToInt", 1);
- const enum_ident = try transCreateNodeIdentifier(c, name);
- const period_tok = try appendToken(c, .Period, ".");
- const field_ident = try transCreateNodeIdentifier(c, field_name);
- const field_access_node = try c.arena.create(ast.Node.SimpleInfixOp);
- field_access_node.* = .{
- .base = .{ .tag = .Period },
- .op_token = period_tok,
- .lhs = enum_ident,
- .rhs = field_ident,
- };
- cast_node.params()[0] = &field_access_node.base;
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- const tld_init_node = &cast_node.base;
- const tld_semicolon_token = try appendToken(c, .Semicolon, ";");
- const tld_node = try ast.Node.VarDecl.create(c.arena, .{
- .name_token = tld_name_tok,
- .mut_token = tld_mut_tok,
- .semicolon_token = tld_semicolon_token,
- }, .{
- .visib_token = tld_visib_tok,
- .eq_token = tld_eq_token,
- .init_node = tld_init_node,
- });
- try addTopLevelDecl(c, field_name, &tld_node.base);
- }
- // make non exhaustive
- const field_node = try c.arena.create(ast.Node.ContainerField);
- field_node.* = .{
- .doc_comments = null,
- .comptime_token = null,
- .name_token = try appendIdentifier(c, "_"),
- .type_expr = null,
- .value_expr = null,
- .align_expr = null,
- };
-
- try fields_and_decls.append(&field_node.base);
- _ = try appendToken(c, .Comma, ",");
- const container_node = try ast.Node.ContainerDecl.alloc(c.arena, fields_and_decls.items.len);
- container_node.* = .{
- .layout_token = extern_tok,
- .kind_token = container_tok,
- .init_arg_expr = init_arg_expr,
- .fields_and_decls_len = fields_and_decls.items.len,
- .lbrace_token = lbrace_token,
- .rbrace_token = try appendToken(c, .RBrace, "}"),
- };
- mem.copy(*ast.Node, container_node.fieldsAndDecls(), fields_and_decls.items);
- break :blk &container_node.base;
- } else
- try transCreateNodeOpaqueType(c);
-
- const semicolon_token = try appendToken(c, .Semicolon, ";");
- const node = try ast.Node.VarDecl.create(c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .visib_token = visib_tok,
- .eq_token = eq_token,
- .init_node = init_node,
- });
-
- try addTopLevelDecl(c, name, &node.base);
- if (!is_unnamed)
- try c.alias_list.append(.{ .alias = bare_name, .name = name });
- return transCreateNodeIdentifier(c, name);
-}
-
-fn createAlias(c: *Context, alias: anytype) !void {
- const visib_tok = try appendToken(c, .Keyword_pub, "pub");
- const mut_tok = try appendToken(c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(c, alias.alias);
- const eq_token = try appendToken(c, .Equal, "=");
- const init_node = try transCreateNodeIdentifier(c, alias.name);
- const semicolon_token = try appendToken(c, .Semicolon, ";");
-
- const node = try ast.Node.VarDecl.create(c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .visib_token = visib_tok,
- .eq_token = eq_token,
- .init_node = init_node,
- });
- return addTopLevelDecl(c, alias.alias, &node.base);
-}
-
-const ResultUsed = enum {
- used,
- unused,
-};
-
-const LRValue = enum {
- l_value,
- r_value,
-};
-
-fn transStmt(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangStmt,
- result_used: ResultUsed,
- lrvalue: LRValue,
-) TransError!*ast.Node {
- const sc = ZigClangStmt_getStmtClass(stmt);
- switch (sc) {
- .BinaryOperatorClass => return transBinaryOperator(rp, scope, @ptrCast(*const ZigClangBinaryOperator, stmt), result_used),
- .CompoundStmtClass => return transCompoundStmt(rp, scope, @ptrCast(*const ZigClangCompoundStmt, stmt)),
- .CStyleCastExprClass => return transCStyleCastExprClass(rp, scope, @ptrCast(*const ZigClangCStyleCastExpr, stmt), result_used, lrvalue),
- .DeclStmtClass => return transDeclStmt(rp, scope, @ptrCast(*const ZigClangDeclStmt, stmt)),
- .DeclRefExprClass => return transDeclRefExpr(rp, scope, @ptrCast(*const ZigClangDeclRefExpr, stmt), lrvalue),
- .ImplicitCastExprClass => return transImplicitCastExpr(rp, scope, @ptrCast(*const ZigClangImplicitCastExpr, stmt), result_used),
- .IntegerLiteralClass => return transIntegerLiteral(rp, scope, @ptrCast(*const ZigClangIntegerLiteral, stmt), result_used, .with_as),
- .ReturnStmtClass => return transReturnStmt(rp, scope, @ptrCast(*const ZigClangReturnStmt, stmt)),
- .StringLiteralClass => return transStringLiteral(rp, scope, @ptrCast(*const ZigClangStringLiteral, stmt), result_used),
- .ParenExprClass => {
- const expr = try transExpr(rp, scope, ZigClangParenExpr_getSubExpr(@ptrCast(*const ZigClangParenExpr, stmt)), .used, lrvalue);
- if (expr.tag == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);
- const node = try rp.c.arena.create(ast.Node.GroupedExpression);
- node.* = .{
- .lparen = try appendToken(rp.c, .LParen, "("),
- .expr = expr,
- .rparen = try appendToken(rp.c, .RParen, ")"),
- };
- return maybeSuppressResult(rp, scope, result_used, &node.base);
- },
- .InitListExprClass => return transInitListExpr(rp, scope, @ptrCast(*const ZigClangInitListExpr, stmt), result_used),
- .ImplicitValueInitExprClass => return transImplicitValueInitExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used),
- .IfStmtClass => return transIfStmt(rp, scope, @ptrCast(*const ZigClangIfStmt, stmt)),
- .WhileStmtClass => return transWhileLoop(rp, scope, @ptrCast(*const ZigClangWhileStmt, stmt)),
- .DoStmtClass => return transDoWhileLoop(rp, scope, @ptrCast(*const ZigClangDoStmt, stmt)),
- .NullStmtClass => {
- const block = try rp.c.createBlock(0);
- block.rbrace = try appendToken(rp.c, .RBrace, "}");
- return &block.base;
- },
- .ContinueStmtClass => return try transCreateNodeContinue(rp.c),
- .BreakStmtClass => return transBreak(rp, scope),
- .ForStmtClass => return transForLoop(rp, scope, @ptrCast(*const ZigClangForStmt, stmt)),
- .FloatingLiteralClass => return transFloatingLiteral(rp, scope, @ptrCast(*const ZigClangFloatingLiteral, stmt), result_used),
- .ConditionalOperatorClass => {
- return transConditionalOperator(rp, scope, @ptrCast(*const ZigClangConditionalOperator, stmt), result_used);
- },
- .BinaryConditionalOperatorClass => {
- return transBinaryConditionalOperator(rp, scope, @ptrCast(*const ZigClangBinaryConditionalOperator, stmt), result_used);
- },
- .SwitchStmtClass => return transSwitch(rp, scope, @ptrCast(*const ZigClangSwitchStmt, stmt)),
- .CaseStmtClass => return transCase(rp, scope, @ptrCast(*const ZigClangCaseStmt, stmt)),
- .DefaultStmtClass => return transDefault(rp, scope, @ptrCast(*const ZigClangDefaultStmt, stmt)),
- .ConstantExprClass => return transConstantExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used),
- .PredefinedExprClass => return transPredefinedExpr(rp, scope, @ptrCast(*const ZigClangPredefinedExpr, stmt), result_used),
- .CharacterLiteralClass => return transCharLiteral(rp, scope, @ptrCast(*const ZigClangCharacterLiteral, stmt), result_used, .with_as),
- .StmtExprClass => return transStmtExpr(rp, scope, @ptrCast(*const ZigClangStmtExpr, stmt), result_used),
- .MemberExprClass => return transMemberExpr(rp, scope, @ptrCast(*const ZigClangMemberExpr, stmt), result_used),
- .ArraySubscriptExprClass => return transArrayAccess(rp, scope, @ptrCast(*const ZigClangArraySubscriptExpr, stmt), result_used),
- .CallExprClass => return transCallExpr(rp, scope, @ptrCast(*const ZigClangCallExpr, stmt), result_used),
- .UnaryExprOrTypeTraitExprClass => return transUnaryExprOrTypeTraitExpr(rp, scope, @ptrCast(*const ZigClangUnaryExprOrTypeTraitExpr, stmt), result_used),
- .UnaryOperatorClass => return transUnaryOperator(rp, scope, @ptrCast(*const ZigClangUnaryOperator, stmt), result_used),
- .CompoundAssignOperatorClass => return transCompoundAssignOperator(rp, scope, @ptrCast(*const ZigClangCompoundAssignOperator, stmt), result_used),
- .OpaqueValueExprClass => {
- const source_expr = ZigClangOpaqueValueExpr_getSourceExpr(@ptrCast(*const ZigClangOpaqueValueExpr, stmt)).?;
- const expr = try transExpr(rp, scope, source_expr, .used, lrvalue);
- if (expr.tag == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);
- const node = try rp.c.arena.create(ast.Node.GroupedExpression);
- node.* = .{
- .lparen = try appendToken(rp.c, .LParen, "("),
- .expr = expr,
- .rparen = try appendToken(rp.c, .RParen, ")"),
- };
- return maybeSuppressResult(rp, scope, result_used, &node.base);
- },
- else => {
- return revertAndWarn(
- rp,
- error.UnsupportedTranslation,
- ZigClangStmt_getBeginLoc(stmt),
- "TODO implement translation of stmt class {}",
- .{@tagName(sc)},
- );
- },
- }
-}
-
-fn transBinaryOperator(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangBinaryOperator,
- result_used: ResultUsed,
-) TransError!*ast.Node {
- const op = ZigClangBinaryOperator_getOpcode(stmt);
- const qt = ZigClangBinaryOperator_getType(stmt);
- var op_token: ast.TokenIndex = undefined;
- var op_id: ast.Node.Tag = undefined;
- switch (op) {
- .Assign => return try transCreateNodeAssign(rp, scope, result_used, ZigClangBinaryOperator_getLHS(stmt), ZigClangBinaryOperator_getRHS(stmt)),
- .Comma => {
- const block_scope = try scope.findBlockScope(rp.c);
- const expr = block_scope.base.parent == scope;
- const lparen = if (expr) try appendToken(rp.c, .LParen, "(") else undefined;
-
- const lhs = try transExpr(rp, &block_scope.base, ZigClangBinaryOperator_getLHS(stmt), .unused, .r_value);
- try block_scope.statements.append(lhs);
-
- const rhs = try transExpr(rp, &block_scope.base, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
- if (expr) {
- _ = try appendToken(rp.c, .Semicolon, ";");
- const break_node = try transCreateNodeBreak(rp.c, block_scope.label, rhs);
- try block_scope.statements.append(&break_node.base);
- const block_node = try block_scope.complete(rp.c);
- const rparen = try appendToken(rp.c, .RParen, ")");
- const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
- grouped_expr.* = .{
- .lparen = lparen,
- .expr = block_node,
- .rparen = rparen,
- };
- return maybeSuppressResult(rp, scope, result_used, &grouped_expr.base);
- } else {
- return maybeSuppressResult(rp, scope, result_used, rhs);
- }
- },
- .Div => {
- if (cIsSignedInteger(qt)) {
- // signed integer division uses @divTrunc
- const div_trunc_node = try rp.c.createBuiltinCall("@divTrunc", 2);
- div_trunc_node.params()[0] = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value);
- _ = try appendToken(rp.c, .Comma, ",");
- const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
- div_trunc_node.params()[1] = rhs;
- div_trunc_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return maybeSuppressResult(rp, scope, result_used, &div_trunc_node.base);
- }
- },
- .Rem => {
- if (cIsSignedInteger(qt)) {
- // signed integer division uses @rem
- const rem_node = try rp.c.createBuiltinCall("@rem", 2);
- rem_node.params()[0] = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value);
- _ = try appendToken(rp.c, .Comma, ",");
- const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
- rem_node.params()[1] = rhs;
- rem_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return maybeSuppressResult(rp, scope, result_used, &rem_node.base);
- }
- },
- .Shl => {
- const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<");
- return maybeSuppressResult(rp, scope, result_used, node);
- },
- .Shr => {
- const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftRight, .AngleBracketAngleBracketRight, ">>");
- return maybeSuppressResult(rp, scope, result_used, node);
- },
- .LAnd => {
- const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolAnd, result_used, true);
- return maybeSuppressResult(rp, scope, result_used, node);
- },
- .LOr => {
- const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolOr, result_used, true);
- return maybeSuppressResult(rp, scope, result_used, node);
- },
- else => {},
- }
- const lhs_node = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value);
- switch (op) {
- .Add => {
- if (cIsUnsignedInteger(qt)) {
- op_token = try appendToken(rp.c, .PlusPercent, "+%");
- op_id = .AddWrap;
- } else {
- op_token = try appendToken(rp.c, .Plus, "+");
- op_id = .Add;
- }
- },
- .Sub => {
- if (cIsUnsignedInteger(qt)) {
- op_token = try appendToken(rp.c, .MinusPercent, "-%");
- op_id = .SubWrap;
- } else {
- op_token = try appendToken(rp.c, .Minus, "-");
- op_id = .Sub;
- }
- },
- .Mul => {
- if (cIsUnsignedInteger(qt)) {
- op_token = try appendToken(rp.c, .AsteriskPercent, "*%");
- op_id = .MulWrap;
- } else {
- op_token = try appendToken(rp.c, .Asterisk, "*");
- op_id = .Mul;
- }
- },
- .Div => {
- // unsigned/float division uses the operator
- op_id = .Div;
- op_token = try appendToken(rp.c, .Slash, "/");
- },
- .Rem => {
- // unsigned/float division uses the operator
- op_id = .Mod;
- op_token = try appendToken(rp.c, .Percent, "%");
- },
- .LT => {
- op_id = .LessThan;
- op_token = try appendToken(rp.c, .AngleBracketLeft, "<");
- },
- .GT => {
- op_id = .GreaterThan;
- op_token = try appendToken(rp.c, .AngleBracketRight, ">");
- },
- .LE => {
- op_id = .LessOrEqual;
- op_token = try appendToken(rp.c, .AngleBracketLeftEqual, "<=");
- },
- .GE => {
- op_id = .GreaterOrEqual;
- op_token = try appendToken(rp.c, .AngleBracketRightEqual, ">=");
- },
- .EQ => {
- op_id = .EqualEqual;
- op_token = try appendToken(rp.c, .EqualEqual, "==");
- },
- .NE => {
- op_id = .BangEqual;
- op_token = try appendToken(rp.c, .BangEqual, "!=");
- },
- .And => {
- op_id = .BitAnd;
- op_token = try appendToken(rp.c, .Ampersand, "&");
- },
- .Xor => {
- op_id = .BitXor;
- op_token = try appendToken(rp.c, .Caret, "^");
- },
- .Or => {
- op_id = .BitOr;
- op_token = try appendToken(rp.c, .Pipe, "|");
- },
- else => unreachable,
- }
-
- const rhs_node = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
-
- const lhs = if (isBoolRes(lhs_node)) init: {
- const cast_node = try rp.c.createBuiltinCall("@boolToInt", 1);
- cast_node.params()[0] = lhs_node;
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- break :init &cast_node.base;
- } else lhs_node;
-
- const rhs = if (isBoolRes(rhs_node)) init: {
- const cast_node = try rp.c.createBuiltinCall("@boolToInt", 1);
- cast_node.params()[0] = rhs_node;
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- break :init &cast_node.base;
- } else rhs_node;
-
- return transCreateNodeInfixOp(rp, scope, lhs, op_id, op_token, rhs, result_used, true);
-}
-
-fn transCompoundStmtInline(
- rp: RestorePoint,
- parent_scope: *Scope,
- stmt: *const ZigClangCompoundStmt,
- block: *Scope.Block,
-) TransError!void {
- var it = ZigClangCompoundStmt_body_begin(stmt);
- const end_it = ZigClangCompoundStmt_body_end(stmt);
- while (it != end_it) : (it += 1) {
- const result = try transStmt(rp, parent_scope, it[0], .unused, .r_value);
- try block.statements.append(result);
- }
-}
-
-fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundStmt) TransError!*ast.Node {
- var block_scope = try Scope.Block.init(rp.c, scope, false);
- defer block_scope.deinit();
- try transCompoundStmtInline(rp, &block_scope.base, stmt, &block_scope);
- return try block_scope.complete(rp.c);
-}
-
-fn transCStyleCastExprClass(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangCStyleCastExpr,
- result_used: ResultUsed,
- lrvalue: LRValue,
-) TransError!*ast.Node {
- const sub_expr = ZigClangCStyleCastExpr_getSubExpr(stmt);
- const cast_node = (try transCCast(
- rp,
- scope,
- ZigClangCStyleCastExpr_getBeginLoc(stmt),
- ZigClangCStyleCastExpr_getType(stmt),
- ZigClangExpr_getType(sub_expr),
- try transExpr(rp, scope, sub_expr, .used, lrvalue),
- ));
- return maybeSuppressResult(rp, scope, result_used, cast_node);
-}
-
-fn transDeclStmtOne(
- rp: RestorePoint,
- scope: *Scope,
- decl: *const ZigClangDecl,
- block_scope: *Scope.Block,
-) TransError!*ast.Node {
- const c = rp.c;
-
- switch (ZigClangDecl_getKind(decl)) {
- .Var => {
- const var_decl = @ptrCast(*const ZigClangVarDecl, decl);
-
- const qual_type = ZigClangVarDecl_getTypeSourceInfo_getType(var_decl);
- const name = try c.str(ZigClangNamedDecl_getName_bytes_begin(
- @ptrCast(*const ZigClangNamedDecl, var_decl),
- ));
- const mangled_name = try block_scope.makeMangledName(c, name);
-
- switch (ZigClangVarDecl_getStorageClass(var_decl)) {
- .Extern, .Static => {
- // This is actually a global variable, put it in the global scope and reference it.
- // `_ = mangled_name;`
- try visitVarDecl(rp.c, var_decl, mangled_name);
- return try maybeSuppressResult(rp, scope, .unused, try transCreateNodeIdentifier(rp.c, mangled_name));
- },
- else => {},
- }
-
- const mut_tok = if (ZigClangQualType_isConstQualified(qual_type))
- try appendToken(c, .Keyword_const, "const")
- else
- try appendToken(c, .Keyword_var, "var");
- const name_tok = try appendIdentifier(c, mangled_name);
-
- _ = try appendToken(c, .Colon, ":");
- const loc = ZigClangDecl_getLocation(decl);
- const type_node = try transQualType(rp, qual_type, loc);
-
- const eq_token = try appendToken(c, .Equal, "=");
- var init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr|
- try transExprCoercing(rp, scope, expr, .used, .r_value)
- else
- try transCreateNodeUndefinedLiteral(c);
- if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) {
- const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);
- builtin_node.params()[0] = init_node;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- init_node = &builtin_node.base;
- }
- const semicolon_token = try appendToken(c, .Semicolon, ";");
- const node = try ast.Node.VarDecl.create(c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .eq_token = eq_token,
- .type_node = type_node,
- .init_node = init_node,
- });
- return &node.base;
- },
- .Typedef => {
- const typedef_decl = @ptrCast(*const ZigClangTypedefNameDecl, decl);
- const name = try c.str(ZigClangNamedDecl_getName_bytes_begin(
- @ptrCast(*const ZigClangNamedDecl, typedef_decl),
- ));
-
- const underlying_qual = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl);
- const underlying_type = ZigClangQualType_getTypePtr(underlying_qual);
-
- const mangled_name = try block_scope.makeMangledName(c, name);
- const node = (try transCreateNodeTypedef(rp, typedef_decl, false, mangled_name)) orelse
- return error.UnsupportedTranslation;
- return node;
- },
- else => |kind| return revertAndWarn(
- rp,
- error.UnsupportedTranslation,
- ZigClangDecl_getLocation(decl),
- "TODO implement translation of DeclStmt kind {}",
- .{@tagName(kind)},
- ),
- }
-}
-
-fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangDeclStmt) TransError!*ast.Node {
- const block_scope = scope.findBlockScope(rp.c) catch unreachable;
-
- var it = ZigClangDeclStmt_decl_begin(stmt);
- const end_it = ZigClangDeclStmt_decl_end(stmt);
- assert(it != end_it);
- while (true) : (it += 1) {
- const node = try transDeclStmtOne(rp, scope, it[0], block_scope);
-
- if (it + 1 == end_it) {
- return node;
- } else {
- try block_scope.statements.append(node);
- }
- }
- unreachable;
-}
-
-fn transDeclRefExpr(
- rp: RestorePoint,
- scope: *Scope,
- expr: *const ZigClangDeclRefExpr,
- lrvalue: LRValue,
-) TransError!*ast.Node {
- const value_decl = ZigClangDeclRefExpr_getDecl(expr);
- const name = try rp.c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, value_decl)));
- const mangled_name = scope.getAlias(name);
- return transCreateNodeIdentifier(rp.c, mangled_name);
-}
-
-fn transImplicitCastExpr(
- rp: RestorePoint,
- scope: *Scope,
- expr: *const ZigClangImplicitCastExpr,
- result_used: ResultUsed,
-) TransError!*ast.Node {
- const c = rp.c;
- const sub_expr = ZigClangImplicitCastExpr_getSubExpr(expr);
- const dest_type = getExprQualType(c, @ptrCast(*const ZigClangExpr, expr));
- const src_type = getExprQualType(c, sub_expr);
- switch (ZigClangImplicitCastExpr_getCastKind(expr)) {
- .BitCast, .FloatingCast, .FloatingToIntegral, .IntegralToFloating, .IntegralCast, .PointerToIntegral, .IntegralToPointer => {
- const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value);
- return try transCCast(rp, scope, ZigClangImplicitCastExpr_getBeginLoc(expr), dest_type, src_type, sub_expr_node);
- },
- .LValueToRValue, .NoOp, .FunctionToPointerDecay => {
- const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value);
- return maybeSuppressResult(rp, scope, result_used, sub_expr_node);
- },
- .ArrayToPointerDecay => {
- if (exprIsStringLiteral(sub_expr)) {
- const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value);
- return maybeSuppressResult(rp, scope, result_used, sub_expr_node);
- }
-
- const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
- prefix_op.rhs = try transExpr(rp, scope, sub_expr, .used, .r_value);
-
- return maybeSuppressResult(rp, scope, result_used, &prefix_op.base);
- },
- .NullToPointer => {
- return try transCreateNodeNullLiteral(rp.c);
- },
- .PointerToBoolean => {
- // @ptrToInt(val) != 0
- const ptr_to_int = try rp.c.createBuiltinCall("@ptrToInt", 1);
- ptr_to_int.params()[0] = try transExpr(rp, scope, sub_expr, .used, .r_value);
- ptr_to_int.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- const op_token = try appendToken(rp.c, .BangEqual, "!=");
- const rhs_node = try transCreateNodeInt(rp.c, 0);
- return transCreateNodeInfixOp(rp, scope, &ptr_to_int.base, .BangEqual, op_token, rhs_node, result_used, false);
- },
- .IntegralToBoolean => {
- const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value);
-
- // The expression is already a boolean one, return it as-is
- if (isBoolRes(sub_expr_node))
- return sub_expr_node;
-
- // val != 0
- const op_token = try appendToken(rp.c, .BangEqual, "!=");
- const rhs_node = try transCreateNodeInt(rp.c, 0);
- return transCreateNodeInfixOp(rp, scope, sub_expr_node, .BangEqual, op_token, rhs_node, result_used, false);
- },
- else => |kind| return revertAndWarn(
- rp,
- error.UnsupportedTranslation,
- ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, expr)),
- "TODO implement translation of CastKind {}",
- .{@tagName(kind)},
- ),
- }
-}
-
-fn transBoolExpr(
- rp: RestorePoint,
- scope: *Scope,
- expr: *const ZigClangExpr,
- used: ResultUsed,
- lrvalue: LRValue,
- grouped: bool,
-) TransError!*ast.Node {
- if (ZigClangStmt_getStmtClass(@ptrCast(*const ZigClangStmt, expr)) == .IntegerLiteralClass) {
- var is_zero: bool = undefined;
- if (!ZigClangIntegerLiteral_isZero(@ptrCast(*const ZigClangIntegerLiteral, expr), &is_zero, rp.c.clang_context)) {
- return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangExpr_getBeginLoc(expr), "invalid integer literal", .{});
- }
- return try transCreateNodeBoolLiteral(rp.c, !is_zero);
- }
-
- const lparen = if (grouped)
- try appendToken(rp.c, .LParen, "(")
- else
- undefined;
- var res = try transExpr(rp, scope, expr, used, lrvalue);
-
- if (isBoolRes(res)) {
- if (!grouped and res.tag == .GroupedExpression) {
- const group = @fieldParentPtr(ast.Node.GroupedExpression, "base", res);
- res = group.expr;
- // get zig fmt to work properly
- tokenSlice(rp.c, group.lparen)[0] = ')';
- }
- return res;
- }
-
- const ty = ZigClangQualType_getTypePtr(getExprQualType(rp.c, expr));
- const node = try finishBoolExpr(rp, scope, ZigClangExpr_getBeginLoc(expr), ty, res, used);
-
- if (grouped) {
- const rparen = try appendToken(rp.c, .RParen, ")");
- const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
- grouped_expr.* = .{
- .lparen = lparen,
- .expr = node,
- .rparen = rparen,
- };
- return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
- } else {
- return maybeSuppressResult(rp, scope, used, node);
- }
-}
-
-fn exprIsBooleanType(expr: *const ZigClangExpr) bool {
- return qualTypeIsBoolean(ZigClangExpr_getType(expr));
-}
-
-fn exprIsStringLiteral(expr: *const ZigClangExpr) bool {
- switch (ZigClangExpr_getStmtClass(expr)) {
- .StringLiteralClass => return true,
- .PredefinedExprClass => return true,
- .UnaryOperatorClass => {
- const op_expr = ZigClangUnaryOperator_getSubExpr(@ptrCast(*const ZigClangUnaryOperator, expr));
- return exprIsStringLiteral(op_expr);
- },
- else => return false,
- }
-}
-
-fn isBoolRes(res: *ast.Node) bool {
- switch (res.tag) {
- .BoolOr,
- .BoolAnd,
- .EqualEqual,
- .BangEqual,
- .LessThan,
- .GreaterThan,
- .LessOrEqual,
- .GreaterOrEqual,
- .BoolNot,
- .BoolLiteral,
- => return true,
-
- .GroupedExpression => return isBoolRes(@fieldParentPtr(ast.Node.GroupedExpression, "base", res).expr),
-
- else => return false,
- }
-}
-
-fn finishBoolExpr(
- rp: RestorePoint,
- scope: *Scope,
- loc: ZigClangSourceLocation,
- ty: *const ZigClangType,
- node: *ast.Node,
- used: ResultUsed,
-) TransError!*ast.Node {
- switch (ZigClangType_getTypeClass(ty)) {
- .Builtin => {
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
-
- switch (ZigClangBuiltinType_getKind(builtin_ty)) {
- .Bool => return node,
- .Char_U,
- .UChar,
- .Char_S,
- .SChar,
- .UShort,
- .UInt,
- .ULong,
- .ULongLong,
- .Short,
- .Int,
- .Long,
- .LongLong,
- .UInt128,
- .Int128,
- .Float,
- .Double,
- .Float128,
- .LongDouble,
- .WChar_U,
- .Char8,
- .Char16,
- .Char32,
- .WChar_S,
- .Float16,
- => {
- const op_token = try appendToken(rp.c, .BangEqual, "!=");
- const rhs_node = try transCreateNodeInt(rp.c, 0);
- return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false);
- },
- .NullPtr => {
- const op_token = try appendToken(rp.c, .EqualEqual, "==");
- const rhs_node = try transCreateNodeNullLiteral(rp.c);
- return transCreateNodeInfixOp(rp, scope, node, .EqualEqual, op_token, rhs_node, used, false);
- },
- else => {},
- }
- },
- .Pointer => {
- const op_token = try appendToken(rp.c, .BangEqual, "!=");
- const rhs_node = try transCreateNodeNullLiteral(rp.c);
- return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false);
- },
- .Typedef => {
- const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty);
- const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty);
- const underlying_type = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl);
- return finishBoolExpr(rp, scope, loc, ZigClangQualType_getTypePtr(underlying_type), node, used);
- },
- .Enum => {
- const op_token = try appendToken(rp.c, .BangEqual, "!=");
- const rhs_node = try transCreateNodeInt(rp.c, 0);
- return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false);
- },
- .Elaborated => {
- const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ty);
- const named_type = ZigClangElaboratedType_getNamedType(elaborated_ty);
- return finishBoolExpr(rp, scope, loc, ZigClangQualType_getTypePtr(named_type), node, used);
- },
- else => {},
- }
- return revertAndWarn(rp, error.UnsupportedType, loc, "unsupported bool expression type", .{});
-}
-
-const SuppressCast = enum {
- with_as,
- no_as,
-};
-fn transIntegerLiteral(
- rp: RestorePoint,
- scope: *Scope,
- expr: *const ZigClangIntegerLiteral,
- result_used: ResultUsed,
- suppress_as: SuppressCast,
-) TransError!*ast.Node {
- var eval_result: ZigClangExprEvalResult = undefined;
- if (!ZigClangIntegerLiteral_EvaluateAsInt(expr, &eval_result, rp.c.clang_context)) {
- const loc = ZigClangIntegerLiteral_getBeginLoc(expr);
- return revertAndWarn(rp, error.UnsupportedTranslation, loc, "invalid integer literal", .{});
- }
-
- if (suppress_as == .no_as) {
- const int_lit_node = try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&eval_result.Val));
- return maybeSuppressResult(rp, scope, result_used, int_lit_node);
- }
-
- // Integer literals in C have types, and this can matter for several reasons.
- // For example, this is valid C:
- // unsigned char y = 256;
- // How this gets evaluated is the 256 is an integer, which gets truncated to signed char, then bit-casted
- // to unsigned char, resulting in 0. In order for this to work, we have to emit this zig code:
- // var y = @bitCast(u8, @truncate(i8, @as(c_int, 256)));
- // Ideally in translate-c we could flatten this out to simply:
- // var y: u8 = 0;
- // But the first step is to be correct, and the next step is to make the output more elegant.
-
- // @as(T, x)
- const expr_base = @ptrCast(*const ZigClangExpr, expr);
- const as_node = try rp.c.createBuiltinCall("@as", 2);
- const ty_node = try transQualType(rp, ZigClangExpr_getType(expr_base), ZigClangExpr_getBeginLoc(expr_base));
- as_node.params()[0] = ty_node;
- _ = try appendToken(rp.c, .Comma, ",");
- as_node.params()[1] = try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&eval_result.Val));
-
- as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return maybeSuppressResult(rp, scope, result_used, &as_node.base);
-}
-
-fn transReturnStmt(
- rp: RestorePoint,
- scope: *Scope,
- expr: *const ZigClangReturnStmt,
-) TransError!*ast.Node {
- const return_kw = try appendToken(rp.c, .Keyword_return, "return");
- const rhs: ?*ast.Node = if (ZigClangReturnStmt_getRetValue(expr)) |val_expr|
- try transExprCoercing(rp, scope, val_expr, .used, .r_value)
- else
- null;
- const return_expr = try ast.Node.ControlFlowExpression.create(rp.c.arena, .{
- .ltoken = return_kw,
- .tag = .Return,
- }, .{
- .rhs = rhs,
- });
- _ = try appendToken(rp.c, .Semicolon, ";");
- return &return_expr.base;
-}
-
-fn transStringLiteral(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangStringLiteral,
- result_used: ResultUsed,
-) TransError!*ast.Node {
- const kind = ZigClangStringLiteral_getKind(stmt);
- switch (kind) {
- .Ascii, .UTF8 => {
- var len: usize = undefined;
- const bytes_ptr = ZigClangStringLiteral_getString_bytes_begin_size(stmt, &len);
- const str = bytes_ptr[0..len];
-
- var char_buf: [4]u8 = undefined;
- len = 0;
- for (str) |c| len += escapeChar(c, &char_buf).len;
-
- const buf = try rp.c.arena.alloc(u8, len + "\"\"".len);
- buf[0] = '"';
- writeEscapedString(buf[1..], str);
- buf[buf.len - 1] = '"';
-
- const token = try appendToken(rp.c, .StringLiteral, buf);
- const node = try rp.c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .StringLiteral },
- .token = token,
- };
- return maybeSuppressResult(rp, scope, result_used, &node.base);
- },
- .UTF16, .UTF32, .Wide => return revertAndWarn(
- rp,
- error.UnsupportedTranslation,
- ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt)),
- "TODO: support string literal kind {}",
- .{kind},
- ),
- }
-}
-
-fn escapedStringLen(s: []const u8) usize {
- var len: usize = 0;
- var char_buf: [4]u8 = undefined;
- for (s) |c| len += escapeChar(c, &char_buf).len;
- return len;
-}
-
-fn writeEscapedString(buf: []u8, s: []const u8) void {
- var char_buf: [4]u8 = undefined;
- var i: usize = 0;
- for (s) |c| {
- const escaped = escapeChar(c, &char_buf);
- mem.copy(u8, buf[i..], escaped);
- i += escaped.len;
- }
-}
-
-// Returns either a string literal or a slice of `buf`.
-fn escapeChar(c: u8, char_buf: *[4]u8) []const u8 {
- return switch (c) {
- '\"' => "\\\"",
- '\'' => "\\'",
- '\\' => "\\\\",
- '\n' => "\\n",
- '\r' => "\\r",
- '\t' => "\\t",
- // Handle the remaining escapes Zig doesn't support by turning them
- // into their respective hex representation
- else => if (std.ascii.isCntrl(c))
- std.fmt.bufPrint(char_buf, "\\x{x:0>2}", .{c}) catch unreachable
- else
- std.fmt.bufPrint(char_buf, "{c}", .{c}) catch unreachable,
- };
-}
-
-fn transCCast(
- rp: RestorePoint,
- scope: *Scope,
- loc: ZigClangSourceLocation,
- dst_type: ZigClangQualType,
- src_type: ZigClangQualType,
- expr: *ast.Node,
-) !*ast.Node {
- if (ZigClangType_isVoidType(qualTypeCanon(dst_type))) return expr;
- if (ZigClangQualType_eq(dst_type, src_type)) return expr;
- if (qualTypeIsPtr(dst_type) and qualTypeIsPtr(src_type))
- return transCPtrCast(rp, loc, dst_type, src_type, expr);
- if (cIsInteger(dst_type) and cIsInteger(src_type)) {
- // 1. Extend or truncate without changing signed-ness.
- // 2. Bit-cast to correct signed-ness
-
- // @bitCast(dest_type, intermediate_value)
- const cast_node = try rp.c.createBuiltinCall("@bitCast", 2);
- cast_node.params()[0] = try transQualType(rp, dst_type, loc);
- _ = try appendToken(rp.c, .Comma, ",");
-
- switch (cIntTypeCmp(dst_type, src_type)) {
- .lt => {
- // @truncate(SameSignSmallerInt, src_type)
- const trunc_node = try rp.c.createBuiltinCall("@truncate", 2);
- const ty_node = try transQualTypeIntWidthOf(rp.c, dst_type, cIsSignedInteger(src_type));
- trunc_node.params()[0] = ty_node;
- _ = try appendToken(rp.c, .Comma, ",");
- trunc_node.params()[1] = expr;
- trunc_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- cast_node.params()[1] = &trunc_node.base;
- },
- .gt => {
- // @as(SameSignBiggerInt, src_type)
- const as_node = try rp.c.createBuiltinCall("@as", 2);
- const ty_node = try transQualTypeIntWidthOf(rp.c, dst_type, cIsSignedInteger(src_type));
- as_node.params()[0] = ty_node;
- _ = try appendToken(rp.c, .Comma, ",");
- as_node.params()[1] = expr;
- as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- cast_node.params()[1] = &as_node.base;
- },
- .eq => {
- cast_node.params()[1] = expr;
- },
- }
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return &cast_node.base;
- }
- if (cIsInteger(dst_type) and qualTypeIsPtr(src_type)) {
- // @intCast(dest_type, @ptrToInt(val))
- const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
- cast_node.params()[0] = try transQualType(rp, dst_type, loc);
- _ = try appendToken(rp.c, .Comma, ",");
- const builtin_node = try rp.c.createBuiltinCall("@ptrToInt", 1);
- builtin_node.params()[0] = expr;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- cast_node.params()[1] = &builtin_node.base;
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return &cast_node.base;
- }
- if (cIsInteger(src_type) and qualTypeIsPtr(dst_type)) {
- // @intToPtr(dest_type, val)
- const builtin_node = try rp.c.createBuiltinCall("@intToPtr", 2);
- builtin_node.params()[0] = try transQualType(rp, dst_type, loc);
- _ = try appendToken(rp.c, .Comma, ",");
- builtin_node.params()[1] = expr;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return &builtin_node.base;
- }
- if (cIsFloating(src_type) and cIsFloating(dst_type)) {
- const builtin_node = try rp.c.createBuiltinCall("@floatCast", 2);
- builtin_node.params()[0] = try transQualType(rp, dst_type, loc);
- _ = try appendToken(rp.c, .Comma, ",");
- builtin_node.params()[1] = expr;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return &builtin_node.base;
- }
- if (cIsFloating(src_type) and !cIsFloating(dst_type)) {
- const builtin_node = try rp.c.createBuiltinCall("@floatToInt", 2);
- builtin_node.params()[0] = try transQualType(rp, dst_type, loc);
- _ = try appendToken(rp.c, .Comma, ",");
- builtin_node.params()[1] = expr;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return &builtin_node.base;
- }
- if (!cIsFloating(src_type) and cIsFloating(dst_type)) {
- const builtin_node = try rp.c.createBuiltinCall("@intToFloat", 2);
- builtin_node.params()[0] = try transQualType(rp, dst_type, loc);
- _ = try appendToken(rp.c, .Comma, ",");
- builtin_node.params()[1] = expr;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return &builtin_node.base;
- }
- if (ZigClangType_isBooleanType(qualTypeCanon(src_type)) and
- !ZigClangType_isBooleanType(qualTypeCanon(dst_type)))
- {
- // @boolToInt returns either a comptime_int or a u1
- const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);
- builtin_node.params()[0] = expr;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- const inner_cast_node = try rp.c.createBuiltinCall("@intCast", 2);
- inner_cast_node.params()[0] = try transCreateNodeIdentifier(rp.c, "u1");
- _ = try appendToken(rp.c, .Comma, ",");
- inner_cast_node.params()[1] = &builtin_node.base;
- inner_cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
- cast_node.params()[0] = try transQualType(rp, dst_type, loc);
- _ = try appendToken(rp.c, .Comma, ",");
-
- if (cIsSignedInteger(dst_type)) {
- const bitcast_node = try rp.c.createBuiltinCall("@bitCast", 2);
- bitcast_node.params()[0] = try transCreateNodeIdentifier(rp.c, "i1");
- _ = try appendToken(rp.c, .Comma, ",");
- bitcast_node.params()[1] = &inner_cast_node.base;
- bitcast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- cast_node.params()[1] = &bitcast_node.base;
- } else {
- cast_node.params()[1] = &inner_cast_node.base;
- }
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- return &cast_node.base;
- }
- if (ZigClangQualType_getTypeClass(ZigClangQualType_getCanonicalType(dst_type)) == .Enum) {
- const builtin_node = try rp.c.createBuiltinCall("@intToEnum", 2);
- builtin_node.params()[0] = try transQualType(rp, dst_type, loc);
- _ = try appendToken(rp.c, .Comma, ",");
- builtin_node.params()[1] = expr;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return &builtin_node.base;
- }
- if (ZigClangQualType_getTypeClass(ZigClangQualType_getCanonicalType(src_type)) == .Enum and
- ZigClangQualType_getTypeClass(ZigClangQualType_getCanonicalType(dst_type)) != .Enum)
- {
- const builtin_node = try rp.c.createBuiltinCall("@enumToInt", 1);
- builtin_node.params()[0] = expr;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return &builtin_node.base;
- }
- const cast_node = try rp.c.createBuiltinCall("@as", 2);
- cast_node.params()[0] = try transQualType(rp, dst_type, loc);
- _ = try appendToken(rp.c, .Comma, ",");
- cast_node.params()[1] = expr;
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return &cast_node.base;
-}
-
-fn transExpr(
- rp: RestorePoint,
- scope: *Scope,
- expr: *const ZigClangExpr,
- used: ResultUsed,
- lrvalue: LRValue,
-) TransError!*ast.Node {
- return transStmt(rp, scope, @ptrCast(*const ZigClangStmt, expr), used, lrvalue);
-}
-
-/// Same as `transExpr` but with the knowledge that the operand will be type coerced, and therefore
-/// an `@as` would be redundant. This is used to prevent redundant `@as` in integer literals.
-fn transExprCoercing(
- rp: RestorePoint,
- scope: *Scope,
- expr: *const ZigClangExpr,
- used: ResultUsed,
- lrvalue: LRValue,
-) TransError!*ast.Node {
- switch (ZigClangStmt_getStmtClass(@ptrCast(*const ZigClangStmt, expr))) {
- .IntegerLiteralClass => {
- return transIntegerLiteral(rp, scope, @ptrCast(*const ZigClangIntegerLiteral, expr), .used, .no_as);
- },
- .CharacterLiteralClass => {
- return transCharLiteral(rp, scope, @ptrCast(*const ZigClangCharacterLiteral, expr), .used, .no_as);
- },
- .UnaryOperatorClass => {
- const un_expr = @ptrCast(*const ZigClangUnaryOperator, expr);
- if (ZigClangUnaryOperator_getOpcode(un_expr) == .Extension) {
- return transExprCoercing(rp, scope, ZigClangUnaryOperator_getSubExpr(un_expr), used, lrvalue);
- }
- },
- else => {},
- }
- return transExpr(rp, scope, expr, .used, .r_value);
-}
-
-fn transInitListExprRecord(
- rp: RestorePoint,
- scope: *Scope,
- loc: ZigClangSourceLocation,
- expr: *const ZigClangInitListExpr,
- ty: *const ZigClangType,
- used: ResultUsed,
-) TransError!*ast.Node {
- var is_union_type = false;
- // Unions and Structs are both represented as RecordDecl
- const record_ty = ZigClangType_getAsRecordType(ty) orelse
- blk: {
- is_union_type = true;
- break :blk ZigClangType_getAsUnionType(ty);
- } orelse unreachable;
- const record_decl = ZigClangRecordType_getDecl(record_ty);
- const record_def = ZigClangRecordDecl_getDefinition(record_decl) orelse
- unreachable;
-
- const ty_node = try transType(rp, ty, loc);
- const init_count = ZigClangInitListExpr_getNumInits(expr);
- var field_inits = std.ArrayList(*ast.Node).init(rp.c.gpa);
- defer field_inits.deinit();
-
- _ = try appendToken(rp.c, .LBrace, "{");
-
- var init_i: c_uint = 0;
- var it = ZigClangRecordDecl_field_begin(record_def);
- const end_it = ZigClangRecordDecl_field_end(record_def);
- while (ZigClangRecordDecl_field_iterator_neq(it, end_it)) : (it = ZigClangRecordDecl_field_iterator_next(it)) {
- const field_decl = ZigClangRecordDecl_field_iterator_deref(it);
-
- // The initializer for a union type has a single entry only
- if (is_union_type and field_decl != ZigClangInitListExpr_getInitializedFieldInUnion(expr)) {
- continue;
- }
-
- assert(init_i < init_count);
- const elem_expr = ZigClangInitListExpr_getInit(expr, init_i);
- init_i += 1;
-
- // Generate the field assignment expression:
- // .field_name = expr
- const period_tok = try appendToken(rp.c, .Period, ".");
-
- var raw_name = try rp.c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, field_decl)));
- if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) {
- const name = rp.c.decl_table.get(@ptrToInt(ZigClangFieldDecl_getCanonicalDecl(field_decl))).?;
- raw_name = try mem.dupe(rp.c.arena, u8, name);
- }
- const field_name_tok = try appendIdentifier(rp.c, raw_name);
-
- _ = try appendToken(rp.c, .Equal, "=");
-
- const field_init_node = try rp.c.arena.create(ast.Node.FieldInitializer);
- field_init_node.* = .{
- .period_token = period_tok,
- .name_token = field_name_tok,
- .expr = try transExpr(rp, scope, elem_expr, .used, .r_value),
- };
-
- try field_inits.append(&field_init_node.base);
- _ = try appendToken(rp.c, .Comma, ",");
- }
-
- const node = try ast.Node.StructInitializer.alloc(rp.c.arena, field_inits.items.len);
- node.* = .{
- .lhs = ty_node,
- .rtoken = try appendToken(rp.c, .RBrace, "}"),
- .list_len = field_inits.items.len,
- };
- mem.copy(*ast.Node, node.list(), field_inits.items);
- return &node.base;
-}
-
-fn transCreateNodeArrayType(
- rp: RestorePoint,
- source_loc: ZigClangSourceLocation,
- ty: *const ZigClangType,
- len: anytype,
-) !*ast.Node {
- const node = try rp.c.arena.create(ast.Node.ArrayType);
- const op_token = try appendToken(rp.c, .LBracket, "[");
- const len_expr = try transCreateNodeInt(rp.c, len);
- _ = try appendToken(rp.c, .RBracket, "]");
- node.* = .{
- .op_token = op_token,
- .rhs = try transType(rp, ty, source_loc),
- .len_expr = len_expr,
- };
- return &node.base;
-}
-
-fn transInitListExprArray(
- rp: RestorePoint,
- scope: *Scope,
- loc: ZigClangSourceLocation,
- expr: *const ZigClangInitListExpr,
- ty: *const ZigClangType,
- used: ResultUsed,
-) TransError!*ast.Node {
- const arr_type = ZigClangType_getAsArrayTypeUnsafe(ty);
- const child_qt = ZigClangArrayType_getElementType(arr_type);
- const init_count = ZigClangInitListExpr_getNumInits(expr);
- assert(ZigClangType_isConstantArrayType(@ptrCast(*const ZigClangType, arr_type)));
- const const_arr_ty = @ptrCast(*const ZigClangConstantArrayType, arr_type);
- const size_ap_int = ZigClangConstantArrayType_getSize(const_arr_ty);
- const all_count = ZigClangAPInt_getLimitedValue(size_ap_int, math.maxInt(usize));
- const leftover_count = all_count - init_count;
-
- var init_node: *ast.Node.ArrayInitializer = undefined;
- var cat_tok: ast.TokenIndex = undefined;
- if (init_count != 0) {
- const ty_node = try transCreateNodeArrayType(
- rp,
- loc,
- ZigClangQualType_getTypePtr(child_qt),
- init_count,
- );
- _ = try appendToken(rp.c, .LBrace, "{");
- init_node = try ast.Node.ArrayInitializer.alloc(rp.c.arena, init_count);
- init_node.* = .{
- .lhs = ty_node,
- .rtoken = undefined,
- .list_len = init_count,
- };
- const init_list = init_node.list();
-
- var i: c_uint = 0;
- while (i < init_count) : (i += 1) {
- const elem_expr = ZigClangInitListExpr_getInit(expr, i);
- init_list[i] = try transExpr(rp, scope, elem_expr, .used, .r_value);
- _ = try appendToken(rp.c, .Comma, ",");
- }
- init_node.rtoken = try appendToken(rp.c, .RBrace, "}");
- if (leftover_count == 0) {
- return &init_node.base;
- }
- cat_tok = try appendToken(rp.c, .PlusPlus, "++");
- }
-
- const ty_node = try transCreateNodeArrayType(rp, loc, ZigClangQualType_getTypePtr(child_qt), 1);
- _ = try appendToken(rp.c, .LBrace, "{");
- const filler_init_node = try ast.Node.ArrayInitializer.alloc(rp.c.arena, 1);
- filler_init_node.* = .{
- .lhs = ty_node,
- .rtoken = undefined,
- .list_len = 1,
- };
- const filler_val_expr = ZigClangInitListExpr_getArrayFiller(expr);
- filler_init_node.list()[0] = try transExpr(rp, scope, filler_val_expr, .used, .r_value);
- filler_init_node.rtoken = try appendToken(rp.c, .RBrace, "}");
-
- const rhs_node = if (leftover_count == 1)
- &filler_init_node.base
- else blk: {
- const mul_tok = try appendToken(rp.c, .AsteriskAsterisk, "**");
- const mul_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
- mul_node.* = .{
- .base = .{ .tag = .ArrayMult },
- .op_token = mul_tok,
- .lhs = &filler_init_node.base,
- .rhs = try transCreateNodeInt(rp.c, leftover_count),
- };
- break :blk &mul_node.base;
- };
-
- if (init_count == 0) {
- return rhs_node;
- }
-
- const cat_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
- cat_node.* = .{
- .base = .{ .tag = .ArrayCat },
- .op_token = cat_tok,
- .lhs = &init_node.base,
- .rhs = rhs_node,
- };
- return &cat_node.base;
-}
-
-fn transInitListExpr(
- rp: RestorePoint,
- scope: *Scope,
- expr: *const ZigClangInitListExpr,
- used: ResultUsed,
-) TransError!*ast.Node {
- const qt = getExprQualType(rp.c, @ptrCast(*const ZigClangExpr, expr));
- var qual_type = ZigClangQualType_getTypePtr(qt);
- const source_loc = ZigClangExpr_getBeginLoc(@ptrCast(*const ZigClangExpr, expr));
-
- if (ZigClangType_isRecordType(qual_type)) {
- return transInitListExprRecord(
- rp,
- scope,
- source_loc,
- expr,
- qual_type,
- used,
- );
- } else if (ZigClangType_isArrayType(qual_type)) {
- return transInitListExprArray(
- rp,
- scope,
- source_loc,
- expr,
- qual_type,
- used,
- );
- } else {
- const type_name = rp.c.str(ZigClangType_getTypeClassName(qual_type));
- return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported initlist type: '{}'", .{type_name});
- }
-}
-
-fn transZeroInitExpr(
- rp: RestorePoint,
- scope: *Scope,
- source_loc: ZigClangSourceLocation,
- ty: *const ZigClangType,
-) TransError!*ast.Node {
- switch (ZigClangType_getTypeClass(ty)) {
- .Builtin => {
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
- switch (ZigClangBuiltinType_getKind(builtin_ty)) {
- .Bool => return try transCreateNodeBoolLiteral(rp.c, false),
- .Char_U,
- .UChar,
- .Char_S,
- .Char8,
- .SChar,
- .UShort,
- .UInt,
- .ULong,
- .ULongLong,
- .Short,
- .Int,
- .Long,
- .LongLong,
- .UInt128,
- .Int128,
- .Float,
- .Double,
- .Float128,
- .Float16,
- .LongDouble,
- => return transCreateNodeInt(rp.c, 0),
- else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}),
- }
- },
- .Pointer => return transCreateNodeNullLiteral(rp.c),
- .Typedef => {
- const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty);
- const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty);
- return transZeroInitExpr(
- rp,
- scope,
- source_loc,
- ZigClangQualType_getTypePtr(
- ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl),
- ),
- );
- },
- else => {},
- }
-
- return revertAndWarn(rp, error.UnsupportedType, source_loc, "type does not have an implicit init value", .{});
-}
-
-fn transImplicitValueInitExpr(
- rp: RestorePoint,
- scope: *Scope,
- expr: *const ZigClangExpr,
- used: ResultUsed,
-) TransError!*ast.Node {
- const source_loc = ZigClangExpr_getBeginLoc(expr);
- const qt = getExprQualType(rp.c, expr);
- const ty = ZigClangQualType_getTypePtr(qt);
- return transZeroInitExpr(rp, scope, source_loc, ty);
-}
-
-fn transIfStmt(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangIfStmt,
-) TransError!*ast.Node {
- // if (c) t
- // if (c) t else e
- const if_node = try transCreateNodeIf(rp.c);
-
- var cond_scope = Scope.Condition{
- .base = .{
- .parent = scope,
- .id = .Condition,
- },
- };
- defer cond_scope.deinit();
- const cond_expr = @ptrCast(*const ZigClangExpr, ZigClangIfStmt_getCond(stmt));
- if_node.condition = try transBoolExpr(rp, &cond_scope.base, cond_expr, .used, .r_value, false);
- _ = try appendToken(rp.c, .RParen, ")");
-
- if_node.body = try transStmt(rp, scope, ZigClangIfStmt_getThen(stmt), .unused, .r_value);
-
- if (ZigClangIfStmt_getElse(stmt)) |expr| {
- if_node.@"else" = try transCreateNodeElse(rp.c);
- if_node.@"else".?.body = try transStmt(rp, scope, expr, .unused, .r_value);
- }
- _ = try appendToken(rp.c, .Semicolon, ";");
- return &if_node.base;
-}
-
-fn transWhileLoop(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangWhileStmt,
-) TransError!*ast.Node {
- const while_node = try transCreateNodeWhile(rp.c);
-
- var cond_scope = Scope.Condition{
- .base = .{
- .parent = scope,
- .id = .Condition,
- },
- };
- defer cond_scope.deinit();
- const cond_expr = @ptrCast(*const ZigClangExpr, ZigClangWhileStmt_getCond(stmt));
- while_node.condition = try transBoolExpr(rp, &cond_scope.base, cond_expr, .used, .r_value, false);
- _ = try appendToken(rp.c, .RParen, ")");
-
- var loop_scope = Scope{
- .parent = scope,
- .id = .Loop,
- };
- while_node.body = try transStmt(rp, &loop_scope, ZigClangWhileStmt_getBody(stmt), .unused, .r_value);
- _ = try appendToken(rp.c, .Semicolon, ";");
- return &while_node.base;
-}
-
-fn transDoWhileLoop(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangDoStmt,
-) TransError!*ast.Node {
- const while_node = try transCreateNodeWhile(rp.c);
-
- while_node.condition = try transCreateNodeBoolLiteral(rp.c, true);
- _ = try appendToken(rp.c, .RParen, ")");
- var new = false;
- var loop_scope = Scope{
- .parent = scope,
- .id = .Loop,
- };
-
- // if (!cond) break;
- const if_node = try transCreateNodeIf(rp.c);
- var cond_scope = Scope.Condition{
- .base = .{
- .parent = scope,
- .id = .Condition,
- },
- };
- defer cond_scope.deinit();
- const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!");
- prefix_op.rhs = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true);
- _ = try appendToken(rp.c, .RParen, ")");
- if_node.condition = &prefix_op.base;
- if_node.body = &(try transCreateNodeBreak(rp.c, null, null)).base;
- _ = try appendToken(rp.c, .Semicolon, ";");
-
- const body_node = if (ZigClangStmt_getStmtClass(ZigClangDoStmt_getBody(stmt)) == .CompoundStmtClass) blk: {
- // there's already a block in C, so we'll append our condition to it.
- // c: do {
- // c: a;
- // c: b;
- // c: } while(c);
- // zig: while (true) {
- // zig: a;
- // zig: b;
- // zig: if (!cond) break;
- // zig: }
- const node = try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value);
- break :blk node.castTag(.Block).?;
- } else blk: {
- // the C statement is without a block, so we need to create a block to contain it.
- // c: do
- // c: a;
- // c: while(c);
- // zig: while (true) {
- // zig: a;
- // zig: if (!cond) break;
- // zig: }
- new = true;
- const block = try rp.c.createBlock(2);
- block.statements_len = 1; // over-allocated so we can add another below
- block.statements()[0] = try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value);
- break :blk block;
- };
-
- // In both cases above, we reserved 1 extra statement.
- body_node.statements_len += 1;
- body_node.statements()[body_node.statements_len - 1] = &if_node.base;
- if (new)
- body_node.rbrace = try appendToken(rp.c, .RBrace, "}");
- while_node.body = &body_node.base;
- return &while_node.base;
-}
-
-fn transForLoop(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangForStmt,
-) TransError!*ast.Node {
- var loop_scope = Scope{
- .parent = scope,
- .id = .Loop,
- };
-
- var block_scope: ?Scope.Block = null;
- defer if (block_scope) |*bs| bs.deinit();
-
- if (ZigClangForStmt_getInit(stmt)) |init| {
- block_scope = try Scope.Block.init(rp.c, scope, false);
- loop_scope.parent = &block_scope.?.base;
- const init_node = try transStmt(rp, &block_scope.?.base, init, .unused, .r_value);
- try block_scope.?.statements.append(init_node);
- }
- var cond_scope = Scope.Condition{
- .base = .{
- .parent = &loop_scope,
- .id = .Condition,
- },
- };
- defer cond_scope.deinit();
-
- const while_node = try transCreateNodeWhile(rp.c);
- while_node.condition = if (ZigClangForStmt_getCond(stmt)) |cond|
- try transBoolExpr(rp, &cond_scope.base, cond, .used, .r_value, false)
- else
- try transCreateNodeBoolLiteral(rp.c, true);
- _ = try appendToken(rp.c, .RParen, ")");
-
- if (ZigClangForStmt_getInc(stmt)) |incr| {
- _ = try appendToken(rp.c, .Colon, ":");
- _ = try appendToken(rp.c, .LParen, "(");
- while_node.continue_expr = try transExpr(rp, &cond_scope.base, incr, .unused, .r_value);
- _ = try appendToken(rp.c, .RParen, ")");
- }
-
- while_node.body = try transStmt(rp, &loop_scope, ZigClangForStmt_getBody(stmt), .unused, .r_value);
- if (block_scope) |*bs| {
- try bs.statements.append(&while_node.base);
- return try bs.complete(rp.c);
- } else {
- _ = try appendToken(rp.c, .Semicolon, ";");
- return &while_node.base;
- }
-}
-
-fn getSwitchCaseCount(stmt: *const ZigClangSwitchStmt) usize {
- const body = ZigClangSwitchStmt_getBody(stmt);
- assert(ZigClangStmt_getStmtClass(body) == .CompoundStmtClass);
- const comp = @ptrCast(*const ZigClangCompoundStmt, body);
- // TODO https://github.com/ziglang/zig/issues/1738
- // return ZigClangCompoundStmt_body_end(comp) - ZigClangCompoundStmt_body_begin(comp);
- const start_addr = @ptrToInt(ZigClangCompoundStmt_body_begin(comp));
- const end_addr = @ptrToInt(ZigClangCompoundStmt_body_end(comp));
- return (end_addr - start_addr) / @sizeOf(*ZigClangStmt);
-}
-
-fn transSwitch(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangSwitchStmt,
-) TransError!*ast.Node {
- const switch_tok = try appendToken(rp.c, .Keyword_switch, "switch");
- _ = try appendToken(rp.c, .LParen, "(");
-
- const cases_len = getSwitchCaseCount(stmt);
-
- var cond_scope = Scope.Condition{
- .base = .{
- .parent = scope,
- .id = .Condition,
- },
- };
- defer cond_scope.deinit();
- const switch_expr = try transExpr(rp, &cond_scope.base, ZigClangSwitchStmt_getCond(stmt), .used, .r_value);
- _ = try appendToken(rp.c, .RParen, ")");
- _ = try appendToken(rp.c, .LBrace, "{");
- // reserve +1 case in case there is no default case
- const switch_node = try ast.Node.Switch.alloc(rp.c.arena, cases_len + 1);
- switch_node.* = .{
- .switch_token = switch_tok,
- .expr = switch_expr,
- .cases_len = cases_len + 1,
- .rbrace = try appendToken(rp.c, .RBrace, "}"),
- };
-
- var switch_scope = Scope.Switch{
- .base = .{
- .id = .Switch,
- .parent = scope,
- },
- .cases = switch_node.cases(),
- .case_index = 0,
- .pending_block = undefined,
- .default_label = null,
- .switch_label = null,
- };
-
- // tmp block that all statements will go before being picked up by a case or default
- var block_scope = try Scope.Block.init(rp.c, &switch_scope.base, false);
- defer block_scope.deinit();
-
- // Note that we do not defer a deinit here; the switch_scope.pending_block field
- // has its own memory management. This resource is freed inside `transCase` and
- // then the final pending_block is freed at the bottom of this function with
- // pending_block.deinit().
- switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false);
- try switch_scope.pending_block.statements.append(&switch_node.base);
-
- const last = try transStmt(rp, &block_scope.base, ZigClangSwitchStmt_getBody(stmt), .unused, .r_value);
- _ = try appendToken(rp.c, .Semicolon, ";");
-
- // take all pending statements
- const last_block_stmts = last.cast(ast.Node.Block).?.statements();
- try switch_scope.pending_block.statements.ensureCapacity(
- switch_scope.pending_block.statements.items.len + last_block_stmts.len,
- );
- for (last_block_stmts) |n| {
- switch_scope.pending_block.statements.appendAssumeCapacity(n);
- }
-
- if (switch_scope.default_label == null) {
- switch_scope.switch_label = try block_scope.makeMangledName(rp.c, "switch");
- }
- if (switch_scope.switch_label) |l| {
- switch_scope.pending_block.label = try appendIdentifier(rp.c, l);
- _ = try appendToken(rp.c, .Colon, ":");
- }
- if (switch_scope.default_label == null) {
- const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c));
- else_prong.expr = blk: {
- var br = try CtrlFlow.init(rp.c, .Break, switch_scope.switch_label.?);
- break :blk &(try br.finish(null)).base;
- };
- _ = try appendToken(rp.c, .Comma, ",");
-
- if (switch_scope.case_index >= switch_scope.cases.len)
- return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt)), "TODO complex switch cases", .{});
- switch_scope.cases[switch_scope.case_index] = &else_prong.base;
- switch_scope.case_index += 1;
- }
- // We overallocated in case there was no default, so now we correct
- // the number of cases in the AST node.
- switch_node.cases_len = switch_scope.case_index;
-
- const result_node = try switch_scope.pending_block.complete(rp.c);
- switch_scope.pending_block.deinit();
- return result_node;
-}
-
-fn transCase(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangCaseStmt,
-) TransError!*ast.Node {
- const block_scope = scope.findBlockScope(rp.c) catch unreachable;
- const switch_scope = scope.getSwitch();
- const label = try block_scope.makeMangledName(rp.c, "case");
- _ = try appendToken(rp.c, .Semicolon, ";");
-
- const expr = if (ZigClangCaseStmt_getRHS(stmt)) |rhs| blk: {
- const lhs_node = try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value);
- const ellips = try appendToken(rp.c, .Ellipsis3, "...");
- const rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
-
- const node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
- node.* = .{
- .base = .{ .tag = .Range },
- .op_token = ellips,
- .lhs = lhs_node,
- .rhs = rhs_node,
- };
- break :blk &node.base;
- } else
- try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value);
-
- const switch_prong = try transCreateNodeSwitchCase(rp.c, expr);
- switch_prong.expr = blk: {
- var br = try CtrlFlow.init(rp.c, .Break, label);
- break :blk &(try br.finish(null)).base;
- };
- _ = try appendToken(rp.c, .Comma, ",");
-
- if (switch_scope.case_index >= switch_scope.cases.len)
- return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt)), "TODO complex switch cases", .{});
- switch_scope.cases[switch_scope.case_index] = &switch_prong.base;
- switch_scope.case_index += 1;
-
- switch_scope.pending_block.label = try appendIdentifier(rp.c, label);
- _ = try appendToken(rp.c, .Colon, ":");
-
- // take all pending statements
- try switch_scope.pending_block.statements.appendSlice(block_scope.statements.items);
- block_scope.statements.shrink(0);
-
- const pending_node = try switch_scope.pending_block.complete(rp.c);
- switch_scope.pending_block.deinit();
- switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false);
-
- try switch_scope.pending_block.statements.append(pending_node);
-
- return transStmt(rp, scope, ZigClangCaseStmt_getSubStmt(stmt), .unused, .r_value);
-}
-
-fn transDefault(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangDefaultStmt,
-) TransError!*ast.Node {
- const block_scope = scope.findBlockScope(rp.c) catch unreachable;
- const switch_scope = scope.getSwitch();
- switch_scope.default_label = try block_scope.makeMangledName(rp.c, "default");
- _ = try appendToken(rp.c, .Semicolon, ";");
-
- const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c));
- else_prong.expr = blk: {
- var br = try CtrlFlow.init(rp.c, .Break, switch_scope.default_label.?);
- break :blk &(try br.finish(null)).base;
- };
- _ = try appendToken(rp.c, .Comma, ",");
-
- if (switch_scope.case_index >= switch_scope.cases.len)
- return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt)), "TODO complex switch cases", .{});
- switch_scope.cases[switch_scope.case_index] = &else_prong.base;
- switch_scope.case_index += 1;
-
- switch_scope.pending_block.label = try appendIdentifier(rp.c, switch_scope.default_label.?);
- _ = try appendToken(rp.c, .Colon, ":");
-
- // take all pending statements
- try switch_scope.pending_block.statements.appendSlice(block_scope.statements.items);
- block_scope.statements.shrink(0);
-
- const pending_node = try switch_scope.pending_block.complete(rp.c);
- switch_scope.pending_block.deinit();
- switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false);
- try switch_scope.pending_block.statements.append(pending_node);
-
- return transStmt(rp, scope, ZigClangDefaultStmt_getSubStmt(stmt), .unused, .r_value);
-}
-
-fn transConstantExpr(rp: RestorePoint, scope: *Scope, expr: *const ZigClangExpr, used: ResultUsed) TransError!*ast.Node {
- var result: ZigClangExprEvalResult = undefined;
- if (!ZigClangExpr_EvaluateAsConstantExpr(expr, &result, .EvaluateForCodeGen, rp.c.clang_context))
- return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangExpr_getBeginLoc(expr), "invalid constant expression", .{});
-
- var val_node: ?*ast.Node = null;
- switch (ZigClangAPValue_getKind(&result.Val)) {
- .Int => {
- // See comment in `transIntegerLiteral` for why this code is here.
- // @as(T, x)
- const expr_base = @ptrCast(*const ZigClangExpr, expr);
- const as_node = try rp.c.createBuiltinCall("@as", 2);
- const ty_node = try transQualType(rp, ZigClangExpr_getType(expr_base), ZigClangExpr_getBeginLoc(expr_base));
- as_node.params()[0] = ty_node;
- _ = try appendToken(rp.c, .Comma, ",");
-
- const int_lit_node = try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&result.Val));
- as_node.params()[1] = int_lit_node;
-
- as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- return maybeSuppressResult(rp, scope, used, &as_node.base);
- },
- else => {
- return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangExpr_getBeginLoc(expr), "unsupported constant expression kind", .{});
- },
- }
-}
-
-fn transPredefinedExpr(rp: RestorePoint, scope: *Scope, expr: *const ZigClangPredefinedExpr, used: ResultUsed) TransError!*ast.Node {
- return transStringLiteral(rp, scope, ZigClangPredefinedExpr_getFunctionName(expr), used);
-}
-
-fn transCharLiteral(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangCharacterLiteral,
- result_used: ResultUsed,
- suppress_as: SuppressCast,
-) TransError!*ast.Node {
- const kind = ZigClangCharacterLiteral_getKind(stmt);
- const int_lit_node = switch (kind) {
- .Ascii, .UTF8 => blk: {
- const val = ZigClangCharacterLiteral_getValue(stmt);
- if (kind == .Ascii) {
- // C has a somewhat obscure feature called multi-character character
- // constant
- if (val > 255)
- break :blk try transCreateNodeInt(rp.c, val);
- }
- var char_buf: [4]u8 = undefined;
- const token = try appendTokenFmt(rp.c, .CharLiteral, "'{}'", .{escapeChar(@intCast(u8, val), &char_buf)});
- const node = try rp.c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .CharLiteral },
- .token = token,
- };
- break :blk &node.base;
- },
- .UTF16, .UTF32, .Wide => return revertAndWarn(
- rp,
- error.UnsupportedTranslation,
- ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt)),
- "TODO: support character literal kind {}",
- .{kind},
- ),
- };
- if (suppress_as == .no_as) {
- return maybeSuppressResult(rp, scope, result_used, int_lit_node);
- }
- // See comment in `transIntegerLiteral` for why this code is here.
- // @as(T, x)
- const expr_base = @ptrCast(*const ZigClangExpr, stmt);
- const as_node = try rp.c.createBuiltinCall("@as", 2);
- const ty_node = try transQualType(rp, ZigClangExpr_getType(expr_base), ZigClangExpr_getBeginLoc(expr_base));
- as_node.params()[0] = ty_node;
- _ = try appendToken(rp.c, .Comma, ",");
- as_node.params()[1] = int_lit_node;
-
- as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return maybeSuppressResult(rp, scope, result_used, &as_node.base);
-}
-
-fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangStmtExpr, used: ResultUsed) TransError!*ast.Node {
- const comp = ZigClangStmtExpr_getSubStmt(stmt);
- if (used == .unused) {
- return transCompoundStmt(rp, scope, comp);
- }
- const lparen = try appendToken(rp.c, .LParen, "(");
- var block_scope = try Scope.Block.init(rp.c, scope, true);
- defer block_scope.deinit();
-
- var it = ZigClangCompoundStmt_body_begin(comp);
- const end_it = ZigClangCompoundStmt_body_end(comp);
- while (it != end_it - 1) : (it += 1) {
- const result = try transStmt(rp, &block_scope.base, it[0], .unused, .r_value);
- try block_scope.statements.append(result);
- }
- const break_node = blk: {
- var tmp = try CtrlFlow.init(rp.c, .Break, "blk");
- const rhs = try transStmt(rp, &block_scope.base, it[0], .used, .r_value);
- break :blk try tmp.finish(rhs);
- };
- _ = try appendToken(rp.c, .Semicolon, ";");
- try block_scope.statements.append(&break_node.base);
- const block_node = try block_scope.complete(rp.c);
- const rparen = try appendToken(rp.c, .RParen, ")");
- const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
- grouped_expr.* = .{
- .lparen = lparen,
- .expr = block_node,
- .rparen = rparen,
- };
- return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
-}
-
-fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangMemberExpr, result_used: ResultUsed) TransError!*ast.Node {
- var container_node = try transExpr(rp, scope, ZigClangMemberExpr_getBase(stmt), .used, .r_value);
-
- if (ZigClangMemberExpr_isArrow(stmt)) {
- container_node = try transCreateNodePtrDeref(rp.c, container_node);
- }
-
- const member_decl = ZigClangMemberExpr_getMemberDecl(stmt);
- const name = blk: {
- const decl_kind = ZigClangDecl_getKind(@ptrCast(*const ZigClangDecl, member_decl));
- // If we're referring to a anonymous struct/enum find the bogus name
- // we've assigned to it during the RecordDecl translation
- if (decl_kind == .Field) {
- const field_decl = @ptrCast(*const struct_ZigClangFieldDecl, member_decl);
- if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) {
- const name = rp.c.decl_table.get(@ptrToInt(ZigClangFieldDecl_getCanonicalDecl(field_decl))).?;
- break :blk try mem.dupe(rp.c.arena, u8, name);
- }
- }
- const decl = @ptrCast(*const ZigClangNamedDecl, member_decl);
- break :blk try rp.c.str(ZigClangNamedDecl_getName_bytes_begin(decl));
- };
-
- const node = try transCreateNodeFieldAccess(rp.c, container_node, name);
- return maybeSuppressResult(rp, scope, result_used, node);
-}
-
-fn transArrayAccess(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangArraySubscriptExpr, result_used: ResultUsed) TransError!*ast.Node {
- var base_stmt = ZigClangArraySubscriptExpr_getBase(stmt);
-
- // Unwrap the base statement if it's an array decayed to a bare pointer type
- // so that we index the array itself
- if (ZigClangStmt_getStmtClass(@ptrCast(*const ZigClangStmt, base_stmt)) == .ImplicitCastExprClass) {
- const implicit_cast = @ptrCast(*const ZigClangImplicitCastExpr, base_stmt);
-
- if (ZigClangImplicitCastExpr_getCastKind(implicit_cast) == .ArrayToPointerDecay) {
- base_stmt = ZigClangImplicitCastExpr_getSubExpr(implicit_cast);
- }
- }
-
- const container_node = try transExpr(rp, scope, base_stmt, .used, .r_value);
- const node = try transCreateNodeArrayAccess(rp.c, container_node);
-
- // cast if the index is long long or signed
- const subscr_expr = ZigClangArraySubscriptExpr_getIdx(stmt);
- const qt = getExprQualType(rp.c, subscr_expr);
- const is_longlong = cIsLongLongInteger(qt);
- const is_signed = cIsSignedInteger(qt);
-
- if (is_longlong or is_signed) {
- const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
- // check if long long first so that signed long long doesn't just become unsigned long long
- var typeid_node = if (is_longlong) try transCreateNodeIdentifier(rp.c, "usize") else try transQualTypeIntWidthOf(rp.c, qt, false);
- cast_node.params()[0] = typeid_node;
- _ = try appendToken(rp.c, .Comma, ",");
- cast_node.params()[1] = try transExpr(rp, scope, subscr_expr, .used, .r_value);
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- node.rtoken = try appendToken(rp.c, .RBrace, "]");
- node.index_expr = &cast_node.base;
- } else {
- node.index_expr = try transExpr(rp, scope, subscr_expr, .used, .r_value);
- node.rtoken = try appendToken(rp.c, .RBrace, "]");
- }
- return maybeSuppressResult(rp, scope, result_used, &node.base);
-}
-
-fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCallExpr, result_used: ResultUsed) TransError!*ast.Node {
- const callee = ZigClangCallExpr_getCallee(stmt);
- var raw_fn_expr = try transExpr(rp, scope, callee, .used, .r_value);
-
- var is_ptr = false;
- const fn_ty = qualTypeGetFnProto(ZigClangExpr_getType(callee), &is_ptr);
-
- const fn_expr = if (is_ptr and fn_ty != null) blk: {
- if (ZigClangExpr_getStmtClass(callee) == .ImplicitCastExprClass) {
- const implicit_cast = @ptrCast(*const ZigClangImplicitCastExpr, callee);
-
- if (ZigClangImplicitCastExpr_getCastKind(implicit_cast) == .FunctionToPointerDecay) {
- const subexpr = ZigClangImplicitCastExpr_getSubExpr(implicit_cast);
- if (ZigClangExpr_getStmtClass(subexpr) == .DeclRefExprClass) {
- const decl_ref = @ptrCast(*const ZigClangDeclRefExpr, subexpr);
- const named_decl = ZigClangDeclRefExpr_getFoundDecl(decl_ref);
- if (ZigClangDecl_getKind(@ptrCast(*const ZigClangDecl, named_decl)) == .Function) {
- break :blk raw_fn_expr;
- }
- }
- }
- }
- break :blk try transCreateNodeUnwrapNull(rp.c, raw_fn_expr);
- } else
- raw_fn_expr;
-
- const num_args = ZigClangCallExpr_getNumArgs(stmt);
- const node = try rp.c.createCall(fn_expr, num_args);
- const call_params = node.params();
-
- const args = ZigClangCallExpr_getArgs(stmt);
- var i: usize = 0;
- while (i < num_args) : (i += 1) {
- if (i != 0) {
- _ = try appendToken(rp.c, .Comma, ",");
- }
- call_params[i] = try transExpr(rp, scope, args[i], .used, .r_value);
- }
- node.rtoken = try appendToken(rp.c, .RParen, ")");
-
- if (fn_ty) |ty| {
- const canon = ZigClangQualType_getCanonicalType(ty.getReturnType());
- const ret_ty = ZigClangQualType_getTypePtr(canon);
- if (ZigClangType_isVoidType(ret_ty)) {
- _ = try appendToken(rp.c, .Semicolon, ";");
- return &node.base;
- }
- }
-
- return maybeSuppressResult(rp, scope, result_used, &node.base);
-}
-
-const ClangFunctionType = union(enum) {
- Proto: *const ZigClangFunctionProtoType,
- NoProto: *const ZigClangFunctionType,
-
- fn getReturnType(self: @This()) ZigClangQualType {
- switch (@as(@TagType(@This()), self)) {
- .Proto => return ZigClangFunctionProtoType_getReturnType(self.Proto),
- .NoProto => return ZigClangFunctionType_getReturnType(self.NoProto),
- }
- }
-};
-
-fn qualTypeGetFnProto(qt: ZigClangQualType, is_ptr: *bool) ?ClangFunctionType {
- const canon = ZigClangQualType_getCanonicalType(qt);
- var ty = ZigClangQualType_getTypePtr(canon);
- is_ptr.* = false;
-
- if (ZigClangType_getTypeClass(ty) == .Pointer) {
- is_ptr.* = true;
- const child_qt = ZigClangType_getPointeeType(ty);
- ty = ZigClangQualType_getTypePtr(child_qt);
- }
- if (ZigClangType_getTypeClass(ty) == .FunctionProto) {
- return ClangFunctionType{ .Proto = @ptrCast(*const ZigClangFunctionProtoType, ty) };
- }
- if (ZigClangType_getTypeClass(ty) == .FunctionNoProto) {
- return ClangFunctionType{ .NoProto = @ptrCast(*const ZigClangFunctionType, ty) };
- }
- return null;
-}
-
-fn transUnaryExprOrTypeTraitExpr(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangUnaryExprOrTypeTraitExpr,
- result_used: ResultUsed,
-) TransError!*ast.Node {
- const loc = ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(stmt);
- const type_node = try transQualType(
- rp,
- ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(stmt),
- loc,
- );
-
- const kind = ZigClangUnaryExprOrTypeTraitExpr_getKind(stmt);
- const kind_str = switch (kind) {
- .SizeOf => "@sizeOf",
- .AlignOf => "@alignOf",
- .PreferredAlignOf,
- .VecStep,
- .OpenMPRequiredSimdAlign,
- => return revertAndWarn(
- rp,
- error.UnsupportedTranslation,
- loc,
- "Unsupported type trait kind {}",
- .{kind},
- ),
- };
-
- const builtin_node = try rp.c.createBuiltinCall(kind_str, 1);
- builtin_node.params()[0] = type_node;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return maybeSuppressResult(rp, scope, result_used, &builtin_node.base);
-}
-
-fn qualTypeHasWrappingOverflow(qt: ZigClangQualType) bool {
- if (cIsUnsignedInteger(qt)) {
- // unsigned integer overflow wraps around.
- return true;
- } else {
- // float, signed integer, and pointer overflow is undefined behavior.
- return false;
- }
-}
-
-fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnaryOperator, used: ResultUsed) TransError!*ast.Node {
- const op_expr = ZigClangUnaryOperator_getSubExpr(stmt);
- switch (ZigClangUnaryOperator_getOpcode(stmt)) {
- .PostInc => if (qualTypeHasWrappingOverflow(ZigClangUnaryOperator_getType(stmt)))
- return transCreatePostCrement(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used)
- else
- return transCreatePostCrement(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", used),
- .PostDec => if (qualTypeHasWrappingOverflow(ZigClangUnaryOperator_getType(stmt)))
- return transCreatePostCrement(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used)
- else
- return transCreatePostCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used),
- .PreInc => if (qualTypeHasWrappingOverflow(ZigClangUnaryOperator_getType(stmt)))
- return transCreatePreCrement(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used)
- else
- return transCreatePreCrement(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", used),
- .PreDec => if (qualTypeHasWrappingOverflow(ZigClangUnaryOperator_getType(stmt)))
- return transCreatePreCrement(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used)
- else
- return transCreatePreCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used),
- .AddrOf => {
- const op_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
- op_node.rhs = try transExpr(rp, scope, op_expr, used, .r_value);
- return &op_node.base;
- },
- .Deref => {
- const value_node = try transExpr(rp, scope, op_expr, used, .r_value);
- var is_ptr = false;
- const fn_ty = qualTypeGetFnProto(ZigClangExpr_getType(op_expr), &is_ptr);
- if (fn_ty != null and is_ptr)
- return value_node;
- const unwrapped = try transCreateNodeUnwrapNull(rp.c, value_node);
- return transCreateNodePtrDeref(rp.c, unwrapped);
- },
- .Plus => return transExpr(rp, scope, op_expr, used, .r_value),
- .Minus => {
- if (!qualTypeHasWrappingOverflow(ZigClangExpr_getType(op_expr))) {
- const op_node = try transCreateNodeSimplePrefixOp(rp.c, .Negation, .Minus, "-");
- op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
- return &op_node.base;
- } else if (cIsUnsignedInteger(ZigClangExpr_getType(op_expr))) {
- // we gotta emit 0 -% x
- const zero = try transCreateNodeInt(rp.c, 0);
- const token = try appendToken(rp.c, .MinusPercent, "-%");
- const expr = try transExpr(rp, scope, op_expr, .used, .r_value);
- return transCreateNodeInfixOp(rp, scope, zero, .SubWrap, token, expr, used, true);
- } else
- return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "C negation with non float non integer", .{});
- },
- .Not => {
- const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BitNot, .Tilde, "~");
- op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
- return &op_node.base;
- },
- .LNot => {
- const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!");
- op_node.rhs = try transBoolExpr(rp, scope, op_expr, .used, .r_value, true);
- return &op_node.base;
- },
- .Extension => {
- return transExpr(rp, scope, ZigClangUnaryOperator_getSubExpr(stmt), used, .l_value);
- },
- else => return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "unsupported C translation {}", .{ZigClangUnaryOperator_getOpcode(stmt)}),
- }
-}
-
-fn transCreatePreCrement(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangUnaryOperator,
- op: ast.Node.Tag,
- op_tok_id: std.zig.Token.Id,
- bytes: []const u8,
- used: ResultUsed,
-) TransError!*ast.Node {
- const op_expr = ZigClangUnaryOperator_getSubExpr(stmt);
-
- if (used == .unused) {
- // common case
- // c: ++expr
- // zig: expr += 1
- const expr = try transExpr(rp, scope, op_expr, .used, .r_value);
- const token = try appendToken(rp.c, op_tok_id, bytes);
- const one = try transCreateNodeInt(rp.c, 1);
- if (scope.id != .Condition)
- _ = try appendToken(rp.c, .Semicolon, ";");
- return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false);
- }
- // worst case
- // c: ++expr
- // zig: (blk: {
- // zig: const _ref = &expr;
- // zig: _ref.* += 1;
- // zig: break :blk _ref.*
- // zig: })
- var block_scope = try Scope.Block.init(rp.c, scope, true);
- defer block_scope.deinit();
- const ref = try block_scope.makeMangledName(rp.c, "ref");
-
- const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(rp.c, ref);
- const eq_token = try appendToken(rp.c, .Equal, "=");
- const rhs_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
- rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
- const init_node = &rhs_node.base;
- const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
- const node = try ast.Node.VarDecl.create(rp.c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .eq_token = eq_token,
- .init_node = init_node,
- });
- try block_scope.statements.append(&node.base);
-
- const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
- const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node);
- _ = try appendToken(rp.c, .Semicolon, ";");
- const token = try appendToken(rp.c, op_tok_id, bytes);
- const one = try transCreateNodeInt(rp.c, 1);
- _ = try appendToken(rp.c, .Semicolon, ";");
- const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false);
- try block_scope.statements.append(assign);
-
- const break_node = try transCreateNodeBreak(rp.c, block_scope.label, ref_node);
- try block_scope.statements.append(&break_node.base);
- const block_node = try block_scope.complete(rp.c);
- // semicolon must immediately follow rbrace because it is the last token in a block
- _ = try appendToken(rp.c, .Semicolon, ";");
- const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
- grouped_expr.* = .{
- .lparen = try appendToken(rp.c, .LParen, "("),
- .expr = block_node,
- .rparen = try appendToken(rp.c, .RParen, ")"),
- };
- return &grouped_expr.base;
-}
-
-fn transCreatePostCrement(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangUnaryOperator,
- op: ast.Node.Tag,
- op_tok_id: std.zig.Token.Id,
- bytes: []const u8,
- used: ResultUsed,
-) TransError!*ast.Node {
- const op_expr = ZigClangUnaryOperator_getSubExpr(stmt);
-
- if (used == .unused) {
- // common case
- // c: ++expr
- // zig: expr += 1
- const expr = try transExpr(rp, scope, op_expr, .used, .r_value);
- const token = try appendToken(rp.c, op_tok_id, bytes);
- const one = try transCreateNodeInt(rp.c, 1);
- if (scope.id != .Condition)
- _ = try appendToken(rp.c, .Semicolon, ";");
- return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false);
- }
- // worst case
- // c: expr++
- // zig: (blk: {
- // zig: const _ref = &expr;
- // zig: const _tmp = _ref.*;
- // zig: _ref.* += 1;
- // zig: break :blk _tmp
- // zig: })
- var block_scope = try Scope.Block.init(rp.c, scope, true);
- defer block_scope.deinit();
- const ref = try block_scope.makeMangledName(rp.c, "ref");
-
- const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(rp.c, ref);
- const eq_token = try appendToken(rp.c, .Equal, "=");
- const rhs_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
- rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
- const init_node = &rhs_node.base;
- const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
- const node = try ast.Node.VarDecl.create(rp.c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .eq_token = eq_token,
- .init_node = init_node,
- });
- try block_scope.statements.append(&node.base);
-
- const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
- const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node);
- _ = try appendToken(rp.c, .Semicolon, ";");
-
- const tmp = try block_scope.makeMangledName(rp.c, "tmp");
- const tmp_mut_tok = try appendToken(rp.c, .Keyword_const, "const");
- const tmp_name_tok = try appendIdentifier(rp.c, tmp);
- const tmp_eq_token = try appendToken(rp.c, .Equal, "=");
- const tmp_init_node = ref_node;
- const tmp_semicolon_token = try appendToken(rp.c, .Semicolon, ";");
- const tmp_node = try ast.Node.VarDecl.create(rp.c.arena, .{
- .name_token = tmp_name_tok,
- .mut_token = tmp_mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .eq_token = tmp_eq_token,
- .init_node = tmp_init_node,
- });
- try block_scope.statements.append(&tmp_node.base);
-
- const token = try appendToken(rp.c, op_tok_id, bytes);
- const one = try transCreateNodeInt(rp.c, 1);
- _ = try appendToken(rp.c, .Semicolon, ";");
- const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false);
- try block_scope.statements.append(assign);
-
- const break_node = blk: {
- var tmp_ctrl_flow = try CtrlFlow.initToken(rp.c, .Break, block_scope.label);
- const rhs = try transCreateNodeIdentifier(rp.c, tmp);
- break :blk try tmp_ctrl_flow.finish(rhs);
- };
- try block_scope.statements.append(&break_node.base);
- _ = try appendToken(rp.c, .Semicolon, ";");
- const block_node = try block_scope.complete(rp.c);
- const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
- grouped_expr.* = .{
- .lparen = try appendToken(rp.c, .LParen, "("),
- .expr = block_node,
- .rparen = try appendToken(rp.c, .RParen, ")"),
- };
- return &grouped_expr.base;
-}
-
-fn transCompoundAssignOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundAssignOperator, used: ResultUsed) TransError!*ast.Node {
- switch (ZigClangCompoundAssignOperator_getOpcode(stmt)) {
- .MulAssign => if (qualTypeHasWrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt)))
- return transCreateCompoundAssign(rp, scope, stmt, .AssignMulWrap, .AsteriskPercentEqual, "*%=", .MulWrap, .AsteriskPercent, "*%", used)
- else
- return transCreateCompoundAssign(rp, scope, stmt, .AssignMul, .AsteriskEqual, "*=", .Mul, .Asterisk, "*", used),
- .AddAssign => if (qualTypeHasWrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt)))
- return transCreateCompoundAssign(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", .AddWrap, .PlusPercent, "+%", used)
- else
- return transCreateCompoundAssign(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", .Add, .Plus, "+", used),
- .SubAssign => if (qualTypeHasWrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt)))
- return transCreateCompoundAssign(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", .SubWrap, .MinusPercent, "-%", used)
- else
- return transCreateCompoundAssign(rp, scope, stmt, .AssignSub, .MinusPercentEqual, "-=", .Sub, .Minus, "-", used),
- .DivAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignDiv, .SlashEqual, "/=", .Div, .Slash, "/", used),
- .RemAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignMod, .PercentEqual, "%=", .Mod, .Percent, "%", used),
- .ShlAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitShiftLeft, .AngleBracketAngleBracketLeftEqual, "<<=", .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<", used),
- .ShrAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitShiftRight, .AngleBracketAngleBracketRightEqual, ">>=", .BitShiftRight, .AngleBracketAngleBracketRight, ">>", used),
- .AndAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitAnd, .AmpersandEqual, "&=", .BitAnd, .Ampersand, "&", used),
- .XorAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitXor, .CaretEqual, "^=", .BitXor, .Caret, "^", used),
- .OrAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitOr, .PipeEqual, "|=", .BitOr, .Pipe, "|", used),
- else => return revertAndWarn(
- rp,
- error.UnsupportedTranslation,
- ZigClangCompoundAssignOperator_getBeginLoc(stmt),
- "unsupported C translation {}",
- .{ZigClangCompoundAssignOperator_getOpcode(stmt)},
- ),
- }
-}
-
-fn transCreateCompoundAssign(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangCompoundAssignOperator,
- assign_op: ast.Node.Tag,
- assign_tok_id: std.zig.Token.Id,
- assign_bytes: []const u8,
- bin_op: ast.Node.Tag,
- bin_tok_id: std.zig.Token.Id,
- bin_bytes: []const u8,
- used: ResultUsed,
-) TransError!*ast.Node {
- const is_shift = bin_op == .BitShiftLeft or bin_op == .BitShiftRight;
- const is_div = bin_op == .Div;
- const is_mod = bin_op == .Mod;
- const lhs = ZigClangCompoundAssignOperator_getLHS(stmt);
- const rhs = ZigClangCompoundAssignOperator_getRHS(stmt);
- const loc = ZigClangCompoundAssignOperator_getBeginLoc(stmt);
- const lhs_qt = getExprQualType(rp.c, lhs);
- const rhs_qt = getExprQualType(rp.c, rhs);
- const is_signed = cIsSignedInteger(lhs_qt);
- const requires_int_cast = blk: {
- const are_integers = cIsInteger(lhs_qt) and cIsInteger(rhs_qt);
- const are_same_sign = cIsSignedInteger(lhs_qt) == cIsSignedInteger(rhs_qt);
- break :blk are_integers and !are_same_sign;
- };
- if (used == .unused) {
- // common case
- // c: lhs += rhs
- // zig: lhs += rhs
- if ((is_mod or is_div) and is_signed) {
- const op_token = try appendToken(rp.c, .Equal, "=");
- const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
- const builtin = if (is_mod) "@rem" else "@divTrunc";
- const builtin_node = try rp.c.createBuiltinCall(builtin, 2);
- const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
- builtin_node.params()[0] = lhs_node;
- _ = try appendToken(rp.c, .Comma, ",");
- builtin_node.params()[1] = try transExpr(rp, scope, rhs, .used, .r_value);
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- op_node.* = .{
- .base = .{ .tag = .Assign },
- .op_token = op_token,
- .lhs = lhs_node,
- .rhs = &builtin_node.base,
- };
- _ = try appendToken(rp.c, .Semicolon, ";");
- return &op_node.base;
- }
-
- const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
- const eq_token = try appendToken(rp.c, assign_tok_id, assign_bytes);
- var rhs_node = if (is_shift or requires_int_cast)
- try transExprCoercing(rp, scope, rhs, .used, .r_value)
- else
- try transExpr(rp, scope, rhs, .used, .r_value);
-
- if (is_shift or requires_int_cast) {
- const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
- const cast_to_type = if (is_shift)
- try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc)
- else
- try transQualType(rp, getExprQualType(rp.c, lhs), loc);
- cast_node.params()[0] = cast_to_type;
- _ = try appendToken(rp.c, .Comma, ",");
- cast_node.params()[1] = rhs_node;
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- rhs_node = &cast_node.base;
- }
- if (scope.id != .Condition)
- _ = try appendToken(rp.c, .Semicolon, ";");
- return transCreateNodeInfixOp(rp, scope, lhs_node, assign_op, eq_token, rhs_node, .used, false);
- }
- // worst case
- // c: lhs += rhs
- // zig: (blk: {
- // zig: const _ref = &lhs;
- // zig: _ref.* = _ref.* + rhs;
- // zig: break :blk _ref.*
- // zig: })
- var block_scope = try Scope.Block.init(rp.c, scope, true);
- defer block_scope.deinit();
- const ref = try block_scope.makeMangledName(rp.c, "ref");
-
- const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(rp.c, ref);
- const eq_token = try appendToken(rp.c, .Equal, "=");
- const addr_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
- addr_node.rhs = try transExpr(rp, scope, lhs, .used, .l_value);
- const init_node = &addr_node.base;
- const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
- const node = try ast.Node.VarDecl.create(rp.c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .eq_token = eq_token,
- .init_node = init_node,
- });
- try block_scope.statements.append(&node.base);
-
- const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
- const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node);
- _ = try appendToken(rp.c, .Semicolon, ";");
-
- if ((is_mod or is_div) and is_signed) {
- const op_token = try appendToken(rp.c, .Equal, "=");
- const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
- const builtin = if (is_mod) "@rem" else "@divTrunc";
- const builtin_node = try rp.c.createBuiltinCall(builtin, 2);
- builtin_node.params()[0] = try transCreateNodePtrDeref(rp.c, lhs_node);
- _ = try appendToken(rp.c, .Comma, ",");
- builtin_node.params()[1] = try transExpr(rp, scope, rhs, .used, .r_value);
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- _ = try appendToken(rp.c, .Semicolon, ";");
- op_node.* = .{
- .base = .{ .tag = .Assign },
- .op_token = op_token,
- .lhs = ref_node,
- .rhs = &builtin_node.base,
- };
- _ = try appendToken(rp.c, .Semicolon, ";");
- try block_scope.statements.append(&op_node.base);
- } else {
- const bin_token = try appendToken(rp.c, bin_tok_id, bin_bytes);
- var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
-
- if (is_shift or requires_int_cast) {
- const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
- const cast_to_type = if (is_shift)
- try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc)
- else
- try transQualType(rp, getExprQualType(rp.c, lhs), loc);
- cast_node.params()[0] = cast_to_type;
- _ = try appendToken(rp.c, .Comma, ",");
- cast_node.params()[1] = rhs_node;
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- rhs_node = &cast_node.base;
- }
-
- const rhs_bin = try transCreateNodeInfixOp(rp, scope, ref_node, bin_op, bin_token, rhs_node, .used, false);
- _ = try appendToken(rp.c, .Semicolon, ";");
-
- const ass_eq_token = try appendToken(rp.c, .Equal, "=");
- const assign = try transCreateNodeInfixOp(rp, scope, ref_node, .Assign, ass_eq_token, rhs_bin, .used, false);
- try block_scope.statements.append(assign);
- }
-
- const break_node = try transCreateNodeBreak(rp.c, block_scope.label, ref_node);
- try block_scope.statements.append(&break_node.base);
- const block_node = try block_scope.complete(rp.c);
- const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
- grouped_expr.* = .{
- .lparen = try appendToken(rp.c, .LParen, "("),
- .expr = block_node,
- .rparen = try appendToken(rp.c, .RParen, ")"),
- };
- return &grouped_expr.base;
-}
-
-fn transCPtrCast(
- rp: RestorePoint,
- loc: ZigClangSourceLocation,
- dst_type: ZigClangQualType,
- src_type: ZigClangQualType,
- expr: *ast.Node,
-) !*ast.Node {
- const ty = ZigClangQualType_getTypePtr(dst_type);
- const child_type = ZigClangType_getPointeeType(ty);
- const src_ty = ZigClangQualType_getTypePtr(src_type);
- const src_child_type = ZigClangType_getPointeeType(src_ty);
-
- if ((ZigClangQualType_isConstQualified(src_child_type) and
- !ZigClangQualType_isConstQualified(child_type)) or
- (ZigClangQualType_isVolatileQualified(src_child_type) and
- !ZigClangQualType_isVolatileQualified(child_type)))
- {
- // Casting away const or volatile requires us to use @intToPtr
- const inttoptr_node = try rp.c.createBuiltinCall("@intToPtr", 2);
- const dst_type_node = try transType(rp, ty, loc);
- inttoptr_node.params()[0] = dst_type_node;
- _ = try appendToken(rp.c, .Comma, ",");
-
- const ptrtoint_node = try rp.c.createBuiltinCall("@ptrToInt", 1);
- ptrtoint_node.params()[0] = expr;
- ptrtoint_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- inttoptr_node.params()[1] = &ptrtoint_node.base;
- inttoptr_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return &inttoptr_node.base;
- } else {
- // Implicit downcasting from higher to lower alignment values is forbidden,
- // use @alignCast to side-step this problem
- const ptrcast_node = try rp.c.createBuiltinCall("@ptrCast", 2);
- const dst_type_node = try transType(rp, ty, loc);
- ptrcast_node.params()[0] = dst_type_node;
- _ = try appendToken(rp.c, .Comma, ",");
-
- if (ZigClangType_isVoidType(qualTypeCanon(child_type))) {
- // void has 1-byte alignment, so @alignCast is not needed
- ptrcast_node.params()[1] = expr;
- } else if (typeIsOpaque(rp.c, qualTypeCanon(child_type), loc)) {
- // For opaque types a ptrCast is enough
- ptrcast_node.params()[1] = expr;
- } else {
- const aligncast_node = try rp.c.createBuiltinCall("@alignCast", 2);
- const alignof_node = try rp.c.createBuiltinCall("@alignOf", 1);
- const child_type_node = try transQualType(rp, child_type, loc);
- alignof_node.params()[0] = child_type_node;
- alignof_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- aligncast_node.params()[0] = &alignof_node.base;
- _ = try appendToken(rp.c, .Comma, ",");
- aligncast_node.params()[1] = expr;
- aligncast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- ptrcast_node.params()[1] = &aligncast_node.base;
- }
- ptrcast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- return &ptrcast_node.base;
- }
-}
-
-fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node {
- const break_scope = scope.getBreakableScope();
- const label_text: ?[]const u8 = if (break_scope.id == .Switch) blk: {
- const swtch = @fieldParentPtr(Scope.Switch, "base", break_scope);
- const block_scope = try scope.findBlockScope(rp.c);
- swtch.switch_label = try block_scope.makeMangledName(rp.c, "switch");
- break :blk swtch.switch_label;
- } else
- null;
-
- var cf = try CtrlFlow.init(rp.c, .Break, label_text);
- const br = try cf.finish(null);
- _ = try appendToken(rp.c, .Semicolon, ";");
- return &br.base;
-}
-
-fn transFloatingLiteral(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangFloatingLiteral, used: ResultUsed) TransError!*ast.Node {
- // TODO use something more accurate
- const dbl = ZigClangAPFloat_getValueAsApproximateDouble(stmt);
- const node = try rp.c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .FloatLiteral },
- .token = try appendTokenFmt(rp.c, .FloatLiteral, "{d}", .{dbl}),
- };
- return maybeSuppressResult(rp, scope, used, &node.base);
-}
-
-fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangBinaryConditionalOperator, used: ResultUsed) TransError!*ast.Node {
- // GNU extension of the ternary operator where the middle expression is
- // omitted, the conditition itself is returned if it evaluates to true
- const casted_stmt = @ptrCast(*const ZigClangAbstractConditionalOperator, stmt);
- const cond_expr = ZigClangAbstractConditionalOperator_getCond(casted_stmt);
- const true_expr = ZigClangAbstractConditionalOperator_getTrueExpr(casted_stmt);
- const false_expr = ZigClangAbstractConditionalOperator_getFalseExpr(casted_stmt);
-
- // c: (cond_expr)?:(false_expr)
- // zig: (blk: {
- // const _cond_temp = (cond_expr);
- // break :blk if (_cond_temp) _cond_temp else (false_expr);
- // })
- const lparen = try appendToken(rp.c, .LParen, "(");
-
- var block_scope = try Scope.Block.init(rp.c, scope, true);
- defer block_scope.deinit();
-
- const mangled_name = try block_scope.makeMangledName(rp.c, "cond_temp");
- const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(rp.c, mangled_name);
- const eq_token = try appendToken(rp.c, .Equal, "=");
- const init_node = try transExpr(rp, &block_scope.base, cond_expr, .used, .r_value);
- const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
- const tmp_var = try ast.Node.VarDecl.create(rp.c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .eq_token = eq_token,
- .init_node = init_node,
- });
- try block_scope.statements.append(&tmp_var.base);
-
- var break_node_tmp = try CtrlFlow.initToken(rp.c, .Break, block_scope.label);
-
- const if_node = try transCreateNodeIf(rp.c);
- var cond_scope = Scope.Condition{
- .base = .{
- .parent = &block_scope.base,
- .id = .Condition,
- },
- };
- defer cond_scope.deinit();
- const tmp_var_node = try transCreateNodeIdentifier(rp.c, mangled_name);
-
- const ty = ZigClangQualType_getTypePtr(getExprQualType(rp.c, cond_expr));
- const cond_node = try finishBoolExpr(rp, &cond_scope.base, ZigClangExpr_getBeginLoc(cond_expr), ty, tmp_var_node, used);
- if_node.condition = cond_node;
- _ = try appendToken(rp.c, .RParen, ")");
-
- if_node.body = try transCreateNodeIdentifier(rp.c, mangled_name);
- if_node.@"else" = try transCreateNodeElse(rp.c);
- if_node.@"else".?.body = try transExpr(rp, &block_scope.base, false_expr, .used, .r_value);
- _ = try appendToken(rp.c, .Semicolon, ";");
-
- const break_node = try break_node_tmp.finish(&if_node.base);
- _ = try appendToken(rp.c, .Semicolon, ";");
- try block_scope.statements.append(&break_node.base);
- const block_node = try block_scope.complete(rp.c);
-
- const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
- grouped_expr.* = .{
- .lparen = lparen,
- .expr = block_node,
- .rparen = try appendToken(rp.c, .RParen, ")"),
- };
- return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
-}
-
-fn transConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangConditionalOperator, used: ResultUsed) TransError!*ast.Node {
- const grouped = scope.id == .Condition;
- const lparen = if (grouped) try appendToken(rp.c, .LParen, "(") else undefined;
- const if_node = try transCreateNodeIf(rp.c);
- var cond_scope = Scope.Condition{
- .base = .{
- .parent = scope,
- .id = .Condition,
- },
- };
- defer cond_scope.deinit();
-
- const casted_stmt = @ptrCast(*const ZigClangAbstractConditionalOperator, stmt);
- const cond_expr = ZigClangAbstractConditionalOperator_getCond(casted_stmt);
- const true_expr = ZigClangAbstractConditionalOperator_getTrueExpr(casted_stmt);
- const false_expr = ZigClangAbstractConditionalOperator_getFalseExpr(casted_stmt);
-
- if_node.condition = try transBoolExpr(rp, &cond_scope.base, cond_expr, .used, .r_value, false);
- _ = try appendToken(rp.c, .RParen, ")");
-
- if_node.body = try transExpr(rp, scope, true_expr, .used, .r_value);
-
- if_node.@"else" = try transCreateNodeElse(rp.c);
- if_node.@"else".?.body = try transExpr(rp, scope, false_expr, .used, .r_value);
-
- if (grouped) {
- const rparen = try appendToken(rp.c, .RParen, ")");
- const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
- grouped_expr.* = .{
- .lparen = lparen,
- .expr = &if_node.base,
- .rparen = rparen,
- };
- return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
- } else {
- return maybeSuppressResult(rp, scope, used, &if_node.base);
- }
-}
-
-fn maybeSuppressResult(
- rp: RestorePoint,
- scope: *Scope,
- used: ResultUsed,
- result: *ast.Node,
-) TransError!*ast.Node {
- if (used == .used) return result;
- if (scope.id != .Condition) {
- // NOTE: This is backwards, but the semicolon must immediately follow the node.
- _ = try appendToken(rp.c, .Semicolon, ";");
- } else { // TODO is there a way to avoid this hack?
- // this parenthesis must come immediately following the node
- _ = try appendToken(rp.c, .RParen, ")");
- // these need to come before _
- _ = try appendToken(rp.c, .Colon, ":");
- _ = try appendToken(rp.c, .LParen, "(");
- }
- const lhs = try transCreateNodeIdentifier(rp.c, "_");
- const op_token = try appendToken(rp.c, .Equal, "=");
- const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
- op_node.* = .{
- .base = .{ .tag = .Assign },
- .op_token = op_token,
- .lhs = lhs,
- .rhs = result,
- };
- return &op_node.base;
-}
-
-fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: *ast.Node) !void {
- try c.root_decls.append(c.gpa, decl_node);
- _ = try c.global_scope.sym_table.put(name, decl_node);
-}
-
-fn transQualType(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) TypeError!*ast.Node {
- return transType(rp, ZigClangQualType_getTypePtr(qt), source_loc);
-}
-
-/// Produces a Zig AST node by translating a Clang QualType, respecting the width, but modifying the signed-ness.
-/// Asserts the type is an integer.
-fn transQualTypeIntWidthOf(c: *Context, ty: ZigClangQualType, is_signed: bool) TypeError!*ast.Node {
- return transTypeIntWidthOf(c, qualTypeCanon(ty), is_signed);
-}
-
-/// Produces a Zig AST node by translating a Clang Type, respecting the width, but modifying the signed-ness.
-/// Asserts the type is an integer.
-fn transTypeIntWidthOf(c: *Context, ty: *const ZigClangType, is_signed: bool) TypeError!*ast.Node {
- assert(ZigClangType_getTypeClass(ty) == .Builtin);
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
- return transCreateNodeIdentifier(c, switch (ZigClangBuiltinType_getKind(builtin_ty)) {
- .Char_U, .Char_S, .UChar, .SChar, .Char8 => if (is_signed) "i8" else "u8",
- .UShort, .Short => if (is_signed) "c_short" else "c_ushort",
- .UInt, .Int => if (is_signed) "c_int" else "c_uint",
- .ULong, .Long => if (is_signed) "c_long" else "c_ulong",
- .ULongLong, .LongLong => if (is_signed) "c_longlong" else "c_ulonglong",
- .UInt128, .Int128 => if (is_signed) "i128" else "u128",
- .Char16 => if (is_signed) "i16" else "u16",
- .Char32 => if (is_signed) "i32" else "u32",
- else => unreachable, // only call this function when it has already been determined the type is int
- });
-}
-
-fn isCBuiltinType(qt: ZigClangQualType, kind: ZigClangBuiltinTypeKind) bool {
- const c_type = qualTypeCanon(qt);
- if (ZigClangType_getTypeClass(c_type) != .Builtin)
- return false;
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type);
- return ZigClangBuiltinType_getKind(builtin_ty) == kind;
-}
-
-fn qualTypeIsPtr(qt: ZigClangQualType) bool {
- return ZigClangType_getTypeClass(qualTypeCanon(qt)) == .Pointer;
-}
-
-fn qualTypeIsBoolean(qt: ZigClangQualType) bool {
- return ZigClangType_isBooleanType(qualTypeCanon(qt));
-}
-
-fn qualTypeIntBitWidth(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) !u32 {
- const ty = ZigClangQualType_getTypePtr(qt);
-
- switch (ZigClangType_getTypeClass(ty)) {
- .Builtin => {
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
-
- switch (ZigClangBuiltinType_getKind(builtin_ty)) {
- .Char_U,
- .UChar,
- .Char_S,
- .SChar,
- => return 8,
- .UInt128,
- .Int128,
- => return 128,
- else => return 0,
- }
-
- unreachable;
- },
- .Typedef => {
- const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty);
- const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty);
- const type_name = try rp.c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, typedef_decl)));
-
- if (mem.eql(u8, type_name, "uint8_t") or mem.eql(u8, type_name, "int8_t")) {
- return 8;
- } else if (mem.eql(u8, type_name, "uint16_t") or mem.eql(u8, type_name, "int16_t")) {
- return 16;
- } else if (mem.eql(u8, type_name, "uint32_t") or mem.eql(u8, type_name, "int32_t")) {
- return 32;
- } else if (mem.eql(u8, type_name, "uint64_t") or mem.eql(u8, type_name, "int64_t")) {
- return 64;
- } else {
- return 0;
- }
- },
- else => return 0,
- }
-
- unreachable;
-}
-
-fn qualTypeToLog2IntRef(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) !*ast.Node {
- const int_bit_width = try qualTypeIntBitWidth(rp, qt, source_loc);
-
- if (int_bit_width != 0) {
- // we can perform the log2 now.
- const cast_bit_width = math.log2_int(u64, int_bit_width);
- const node = try rp.c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .IntegerLiteral },
- .token = try appendTokenFmt(rp.c, .Identifier, "u{}", .{cast_bit_width}),
- };
- return &node.base;
- }
-
- const zig_type_node = try transQualType(rp, qt, source_loc);
-
- // @import("std").math.Log2Int(c_long);
- //
- // FnCall
- // FieldAccess
- // FieldAccess
- // FnCall (.builtin = true)
- // Symbol "import"
- // StringLiteral "std"
- // Symbol "math"
- // Symbol "Log2Int"
- // Symbol (var from above)
-
- const import_fn_call = try rp.c.createBuiltinCall("@import", 1);
- const std_token = try appendToken(rp.c, .StringLiteral, "\"std\"");
- const std_node = try rp.c.arena.create(ast.Node.OneToken);
- std_node.* = .{
- .base = .{ .tag = .StringLiteral },
- .token = std_token,
- };
- import_fn_call.params()[0] = &std_node.base;
- import_fn_call.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- const inner_field_access = try transCreateNodeFieldAccess(rp.c, &import_fn_call.base, "math");
- const outer_field_access = try transCreateNodeFieldAccess(rp.c, inner_field_access, "Log2Int");
- const log2int_fn_call = try rp.c.createCall(outer_field_access, 1);
- log2int_fn_call.params()[0] = zig_type_node;
- log2int_fn_call.rtoken = try appendToken(rp.c, .RParen, ")");
-
- return &log2int_fn_call.base;
-}
-
-fn qualTypeChildIsFnProto(qt: ZigClangQualType) bool {
- const ty = qualTypeCanon(qt);
-
- switch (ZigClangType_getTypeClass(ty)) {
- .FunctionProto, .FunctionNoProto => return true,
- else => return false,
- }
-}
-
-fn qualTypeCanon(qt: ZigClangQualType) *const ZigClangType {
- const canon = ZigClangQualType_getCanonicalType(qt);
- return ZigClangQualType_getTypePtr(canon);
-}
-
-fn getExprQualType(c: *Context, expr: *const ZigClangExpr) ZigClangQualType {
- blk: {
- // If this is a C `char *`, turn it into a `const char *`
- if (ZigClangExpr_getStmtClass(expr) != .ImplicitCastExprClass) break :blk;
- const cast_expr = @ptrCast(*const ZigClangImplicitCastExpr, expr);
- if (ZigClangImplicitCastExpr_getCastKind(cast_expr) != .ArrayToPointerDecay) break :blk;
- const sub_expr = ZigClangImplicitCastExpr_getSubExpr(cast_expr);
- if (ZigClangExpr_getStmtClass(sub_expr) != .StringLiteralClass) break :blk;
- const array_qt = ZigClangExpr_getType(sub_expr);
- const array_type = @ptrCast(*const ZigClangArrayType, ZigClangQualType_getTypePtr(array_qt));
- var pointee_qt = ZigClangArrayType_getElementType(array_type);
- ZigClangQualType_addConst(&pointee_qt);
- return ZigClangASTContext_getPointerType(c.clang_context, pointee_qt);
- }
- return ZigClangExpr_getType(expr);
-}
-
-fn typeIsOpaque(c: *Context, ty: *const ZigClangType, loc: ZigClangSourceLocation) bool {
- switch (ZigClangType_getTypeClass(ty)) {
- .Builtin => {
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
- return ZigClangBuiltinType_getKind(builtin_ty) == .Void;
- },
- .Record => {
- const record_ty = @ptrCast(*const ZigClangRecordType, ty);
- const record_decl = ZigClangRecordType_getDecl(record_ty);
- const record_def = ZigClangRecordDecl_getDefinition(record_decl) orelse
- return true;
- var it = ZigClangRecordDecl_field_begin(record_def);
- const end_it = ZigClangRecordDecl_field_end(record_def);
- while (ZigClangRecordDecl_field_iterator_neq(it, end_it)) : (it = ZigClangRecordDecl_field_iterator_next(it)) {
- const field_decl = ZigClangRecordDecl_field_iterator_deref(it);
-
- if (ZigClangFieldDecl_isBitField(field_decl)) {
- return true;
- }
- }
- return false;
- },
- .Elaborated => {
- const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ty);
- const qt = ZigClangElaboratedType_getNamedType(elaborated_ty);
- return typeIsOpaque(c, ZigClangQualType_getTypePtr(qt), loc);
- },
- .Typedef => {
- const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty);
- const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty);
- const underlying_type = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl);
- return typeIsOpaque(c, ZigClangQualType_getTypePtr(underlying_type), loc);
- },
- else => return false,
- }
-}
-
-fn cIsInteger(qt: ZigClangQualType) bool {
- return cIsSignedInteger(qt) or cIsUnsignedInteger(qt);
-}
-
-fn cIsUnsignedInteger(qt: ZigClangQualType) bool {
- const c_type = qualTypeCanon(qt);
- if (ZigClangType_getTypeClass(c_type) != .Builtin) return false;
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type);
- return switch (ZigClangBuiltinType_getKind(builtin_ty)) {
- .Char_U,
- .UChar,
- .Char_S,
- .UShort,
- .UInt,
- .ULong,
- .ULongLong,
- .UInt128,
- .WChar_U,
- => true,
- else => false,
- };
-}
-
-fn cIntTypeToIndex(qt: ZigClangQualType) u8 {
- const c_type = qualTypeCanon(qt);
- assert(ZigClangType_getTypeClass(c_type) == .Builtin);
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type);
- return switch (ZigClangBuiltinType_getKind(builtin_ty)) {
- .Bool, .Char_U, .Char_S, .UChar, .SChar, .Char8 => 1,
- .WChar_U, .WChar_S => 2,
- .UShort, .Short, .Char16 => 3,
- .UInt, .Int, .Char32 => 4,
- .ULong, .Long => 5,
- .ULongLong, .LongLong => 6,
- .UInt128, .Int128 => 7,
- else => unreachable,
- };
-}
-
-fn cIntTypeCmp(a: ZigClangQualType, b: ZigClangQualType) math.Order {
- const a_index = cIntTypeToIndex(a);
- const b_index = cIntTypeToIndex(b);
- return math.order(a_index, b_index);
-}
-
-fn cIsSignedInteger(qt: ZigClangQualType) bool {
- const c_type = qualTypeCanon(qt);
- if (ZigClangType_getTypeClass(c_type) != .Builtin) return false;
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type);
- return switch (ZigClangBuiltinType_getKind(builtin_ty)) {
- .SChar,
- .Short,
- .Int,
- .Long,
- .LongLong,
- .Int128,
- .WChar_S,
- => true,
- else => false,
- };
-}
-
-fn cIsFloating(qt: ZigClangQualType) bool {
- const c_type = qualTypeCanon(qt);
- if (ZigClangType_getTypeClass(c_type) != .Builtin) return false;
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type);
- return switch (ZigClangBuiltinType_getKind(builtin_ty)) {
- .Float,
- .Double,
- .Float128,
- .LongDouble,
- => true,
- else => false,
- };
-}
-
-fn cIsLongLongInteger(qt: ZigClangQualType) bool {
- const c_type = qualTypeCanon(qt);
- if (ZigClangType_getTypeClass(c_type) != .Builtin) return false;
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type);
- return switch (ZigClangBuiltinType_getKind(builtin_ty)) {
- .LongLong, .ULongLong, .Int128, .UInt128 => true,
- else => false,
- };
-}
-fn transCreateNodeAssign(
- rp: RestorePoint,
- scope: *Scope,
- result_used: ResultUsed,
- lhs: *const ZigClangExpr,
- rhs: *const ZigClangExpr,
-) !*ast.Node {
- // common case
- // c: lhs = rhs
- // zig: lhs = rhs
- if (result_used == .unused) {
- const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
- const eq_token = try appendToken(rp.c, .Equal, "=");
- var rhs_node = try transExprCoercing(rp, scope, rhs, .used, .r_value);
- if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) {
- const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);
- builtin_node.params()[0] = rhs_node;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- rhs_node = &builtin_node.base;
- }
- if (scope.id != .Condition)
- _ = try appendToken(rp.c, .Semicolon, ";");
- return transCreateNodeInfixOp(rp, scope, lhs_node, .Assign, eq_token, rhs_node, .used, false);
- }
-
- // worst case
- // c: lhs = rhs
- // zig: (blk: {
- // zig: const _tmp = rhs;
- // zig: lhs = _tmp;
- // zig: break :blk _tmp
- // zig: })
- var block_scope = try Scope.Block.init(rp.c, scope, true);
- defer block_scope.deinit();
-
- const tmp = try block_scope.makeMangledName(rp.c, "tmp");
- const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(rp.c, tmp);
- const eq_token = try appendToken(rp.c, .Equal, "=");
- var rhs_node = try transExpr(rp, &block_scope.base, rhs, .used, .r_value);
- if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) {
- const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);
- builtin_node.params()[0] = rhs_node;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- rhs_node = &builtin_node.base;
- }
- const init_node = rhs_node;
- const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
- const node = try ast.Node.VarDecl.create(rp.c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .eq_token = eq_token,
- .init_node = init_node,
- });
- try block_scope.statements.append(&node.base);
-
- const lhs_node = try transExpr(rp, &block_scope.base, lhs, .used, .l_value);
- const lhs_eq_token = try appendToken(rp.c, .Equal, "=");
- const ident = try transCreateNodeIdentifier(rp.c, tmp);
- _ = try appendToken(rp.c, .Semicolon, ";");
-
- const assign = try transCreateNodeInfixOp(rp, &block_scope.base, lhs_node, .Assign, lhs_eq_token, ident, .used, false);
- try block_scope.statements.append(assign);
-
- const break_node = blk: {
- var tmp_ctrl_flow = try CtrlFlow.init(rp.c, .Break, tokenSlice(rp.c, block_scope.label.?));
- const rhs_expr = try transCreateNodeIdentifier(rp.c, tmp);
- break :blk try tmp_ctrl_flow.finish(rhs_expr);
- };
- _ = try appendToken(rp.c, .Semicolon, ";");
- try block_scope.statements.append(&break_node.base);
- const block_node = try block_scope.complete(rp.c);
- // semicolon must immediately follow rbrace because it is the last token in a block
- _ = try appendToken(rp.c, .Semicolon, ";");
- return block_node;
-}
-
-fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []const u8) !*ast.Node {
- const field_access_node = try c.arena.create(ast.Node.SimpleInfixOp);
- field_access_node.* = .{
- .base = .{ .tag = .Period },
- .op_token = try appendToken(c, .Period, "."),
- .lhs = container,
- .rhs = try transCreateNodeIdentifier(c, field_name),
- };
- return &field_access_node.base;
-}
-
-fn transCreateNodeSimplePrefixOp(
- c: *Context,
- comptime tag: ast.Node.Tag,
- op_tok_id: std.zig.Token.Id,
- bytes: []const u8,
-) !*ast.Node.SimplePrefixOp {
- const node = try c.arena.create(ast.Node.SimplePrefixOp);
- node.* = .{
- .base = .{ .tag = tag },
- .op_token = try appendToken(c, op_tok_id, bytes),
- .rhs = undefined, // translate and set afterward
- };
- return node;
-}
-
-fn transCreateNodeInfixOp(
- rp: RestorePoint,
- scope: *Scope,
- lhs_node: *ast.Node,
- op: ast.Node.Tag,
- op_token: ast.TokenIndex,
- rhs_node: *ast.Node,
- used: ResultUsed,
- grouped: bool,
-) !*ast.Node {
- var lparen = if (grouped)
- try appendToken(rp.c, .LParen, "(")
- else
- null;
- const node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
- node.* = .{
- .base = .{ .tag = op },
- .op_token = op_token,
- .lhs = lhs_node,
- .rhs = rhs_node,
- };
- if (!grouped) return maybeSuppressResult(rp, scope, used, &node.base);
- const rparen = try appendToken(rp.c, .RParen, ")");
- const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
- grouped_expr.* = .{
- .lparen = lparen.?,
- .expr = &node.base,
- .rparen = rparen,
- };
- return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
-}
-
-fn transCreateNodeBoolInfixOp(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangBinaryOperator,
- op: ast.Node.Tag,
- used: ResultUsed,
- grouped: bool,
-) !*ast.Node {
- std.debug.assert(op == .BoolAnd or op == .BoolOr);
-
- const lhs_hode = try transBoolExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value, true);
- const op_token = if (op == .BoolAnd)
- try appendToken(rp.c, .Keyword_and, "and")
- else
- try appendToken(rp.c, .Keyword_or, "or");
- const rhs = try transBoolExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value, true);
-
- return transCreateNodeInfixOp(
- rp,
- scope,
- lhs_hode,
- op,
- op_token,
- rhs,
- used,
- grouped,
- );
-}
-
-fn transCreateNodePtrType(
- c: *Context,
- is_const: bool,
- is_volatile: bool,
- op_tok_id: std.zig.Token.Id,
-) !*ast.Node.PtrType {
- const node = try c.arena.create(ast.Node.PtrType);
- const op_token = switch (op_tok_id) {
- .LBracket => blk: {
- const lbracket = try appendToken(c, .LBracket, "[");
- _ = try appendToken(c, .Asterisk, "*");
- _ = try appendToken(c, .RBracket, "]");
- break :blk lbracket;
- },
- .Identifier => blk: {
- const lbracket = try appendToken(c, .LBracket, "["); // Rendering checks if this token + 2 == .Identifier, so needs to return this token
- _ = try appendToken(c, .Asterisk, "*");
- _ = try appendIdentifier(c, "c");
- _ = try appendToken(c, .RBracket, "]");
- break :blk lbracket;
- },
- .Asterisk => try appendToken(c, .Asterisk, "*"),
- else => unreachable,
- };
- node.* = .{
- .op_token = op_token,
- .ptr_info = .{
- .const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null,
- .volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null,
- },
- .rhs = undefined, // translate and set afterward
- };
- return node;
-}
-
-fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node {
- const num_limbs = math.cast(usize, ZigClangAPSInt_getNumWords(int)) catch |err| switch (err) {
- error.Overflow => return error.OutOfMemory,
- };
- var aps_int = int;
- const is_negative = ZigClangAPSInt_isSigned(int) and ZigClangAPSInt_isNegative(int);
- if (is_negative) aps_int = ZigClangAPSInt_negate(aps_int);
- defer if (is_negative) {
- ZigClangAPSInt_free(aps_int);
- };
-
- const limbs = try c.arena.alloc(math.big.Limb, num_limbs);
- defer c.arena.free(limbs);
-
- const data = ZigClangAPSInt_getRawData(aps_int);
- switch (@sizeOf(math.big.Limb)) {
- 8 => {
- var i: usize = 0;
- while (i < num_limbs) : (i += 1) {
- limbs[i] = data[i];
- }
- },
- 4 => {
- var limb_i: usize = 0;
- var data_i: usize = 0;
- while (limb_i < num_limbs) : ({
- limb_i += 2;
- data_i += 1;
- }) {
- limbs[limb_i] = @truncate(u32, data[data_i]);
- limbs[limb_i + 1] = @truncate(u32, data[data_i] >> 32);
- }
- },
- else => @compileError("unimplemented"),
- }
-
- const big: math.big.int.Const = .{ .limbs = limbs, .positive = !is_negative };
- const str = big.toStringAlloc(c.arena, 10, false) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- };
- defer c.arena.free(str);
- const token = try appendToken(c, .IntegerLiteral, str);
- const node = try c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .IntegerLiteral },
- .token = token,
- };
- return &node.base;
-}
-
-fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node {
- const token = try appendToken(c, .Keyword_undefined, "undefined");
- const node = try c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .UndefinedLiteral },
- .token = token,
- };
- return &node.base;
-}
-
-fn transCreateNodeNullLiteral(c: *Context) !*ast.Node {
- const token = try appendToken(c, .Keyword_null, "null");
- const node = try c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .NullLiteral },
- .token = token,
- };
- return &node.base;
-}
-
-fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node {
- const token = if (value)
- try appendToken(c, .Keyword_true, "true")
- else
- try appendToken(c, .Keyword_false, "false");
- const node = try c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .BoolLiteral },
- .token = token,
- };
- return &node.base;
-}
-
-fn transCreateNodeInt(c: *Context, int: anytype) !*ast.Node {
- const token = try appendTokenFmt(c, .IntegerLiteral, "{}", .{int});
- const node = try c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .IntegerLiteral },
- .token = token,
- };
- return &node.base;
-}
-
-fn transCreateNodeFloat(c: *Context, int: anytype) !*ast.Node {
- const token = try appendTokenFmt(c, .FloatLiteral, "{}", .{int});
- const node = try c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .FloatLiteral },
- .token = token,
- };
- return &node.base;
-}
-
-fn transCreateNodeOpaqueType(c: *Context) !*ast.Node {
- const call_node = try c.createBuiltinCall("@Type", 1);
- call_node.params()[0] = try transCreateNodeEnumLiteral(c, "Opaque");
- call_node.rparen_token = try appendToken(c, .RParen, ")");
- return &call_node.base;
-}
-
-fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_alias: *ast.Node.FnProto) !*ast.Node {
- const scope = &c.global_scope.base;
-
- const pub_tok = try appendToken(c, .Keyword_pub, "pub");
- const inline_tok = try appendToken(c, .Keyword_inline, "inline");
- const fn_tok = try appendToken(c, .Keyword_fn, "fn");
- const name_tok = try appendIdentifier(c, name);
- _ = try appendToken(c, .LParen, "(");
-
- var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(c.gpa);
- defer fn_params.deinit();
-
- for (proto_alias.params()) |param, i| {
- if (i != 0) {
- _ = try appendToken(c, .Comma, ",");
- }
- const param_name_tok = param.name_token orelse
- try appendTokenFmt(c, .Identifier, "arg_{}", .{c.getMangle()});
-
- _ = try appendToken(c, .Colon, ":");
-
- (try fn_params.addOne()).* = .{
- .doc_comments = null,
- .comptime_token = null,
- .noalias_token = param.noalias_token,
- .name_token = param_name_tok,
- .param_type = param.param_type,
- };
- }
-
- _ = try appendToken(c, .RParen, ")");
-
- const block_lbrace = try appendToken(c, .LBrace, "{");
-
- const return_kw = try appendToken(c, .Keyword_return, "return");
- const unwrap_expr = try transCreateNodeUnwrapNull(c, ref.cast(ast.Node.VarDecl).?.getInitNode().?);
-
- const call_expr = try c.createCall(unwrap_expr, fn_params.items.len);
- const call_params = call_expr.params();
-
- for (fn_params.items) |param, i| {
- if (i != 0) {
- _ = try appendToken(c, .Comma, ",");
- }
- call_params[i] = try transCreateNodeIdentifier(c, tokenSlice(c, param.name_token.?));
- }
- call_expr.rtoken = try appendToken(c, .RParen, ")");
-
- const return_expr = try ast.Node.ControlFlowExpression.create(c.arena, .{
- .ltoken = return_kw,
- .tag = .Return,
- }, .{
- .rhs = &call_expr.base,
- });
- _ = try appendToken(c, .Semicolon, ";");
-
- const block = try ast.Node.Block.alloc(c.arena, 1);
- block.* = .{
- .lbrace = block_lbrace,
- .statements_len = 1,
- .rbrace = try appendToken(c, .RBrace, "}"),
- };
- block.statements()[0] = &return_expr.base;
-
- const fn_proto = try ast.Node.FnProto.create(c.arena, .{
- .params_len = fn_params.items.len,
- .fn_token = fn_tok,
- .return_type = proto_alias.return_type,
- }, .{
- .visib_token = pub_tok,
- .name_token = name_tok,
- .extern_export_inline_token = inline_tok,
- .body_node = &block.base,
- });
- mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
- return &fn_proto.base;
-}
-
-fn transCreateNodeUnwrapNull(c: *Context, wrapped: *ast.Node) !*ast.Node {
- _ = try appendToken(c, .Period, ".");
- const qm = try appendToken(c, .QuestionMark, "?");
- const node = try c.arena.create(ast.Node.SimpleSuffixOp);
- node.* = .{
- .base = .{ .tag = .UnwrapOptional },
- .lhs = wrapped,
- .rtoken = qm,
- };
- return &node.base;
-}
-
-fn transCreateNodeEnumLiteral(c: *Context, name: []const u8) !*ast.Node {
- const node = try c.arena.create(ast.Node.EnumLiteral);
- node.* = .{
- .dot = try appendToken(c, .Period, "."),
- .name = try appendIdentifier(c, name),
- };
- return &node.base;
-}
-
-fn transCreateNodeStringLiteral(c: *Context, str: []const u8) !*ast.Node {
- const node = try c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .StringLiteral },
- .token = try appendToken(c, .StringLiteral, str),
- };
- return &node.base;
-}
-
-fn transCreateNodeIf(c: *Context) !*ast.Node.If {
- const if_tok = try appendToken(c, .Keyword_if, "if");
- _ = try appendToken(c, .LParen, "(");
- const node = try c.arena.create(ast.Node.If);
- node.* = .{
- .if_token = if_tok,
- .condition = undefined,
- .payload = null,
- .body = undefined,
- .@"else" = null,
- };
- return node;
-}
-
-fn transCreateNodeElse(c: *Context) !*ast.Node.Else {
- const node = try c.arena.create(ast.Node.Else);
- node.* = .{
- .else_token = try appendToken(c, .Keyword_else, "else"),
- .payload = null,
- .body = undefined,
- };
- return node;
-}
-
-fn transCreateNodeBreak(
- c: *Context,
- label: ?ast.TokenIndex,
- rhs: ?*ast.Node,
-) !*ast.Node.ControlFlowExpression {
- var ctrl_flow = try CtrlFlow.init(c, .Break, if (label) |l| tokenSlice(c, l) else null);
- return ctrl_flow.finish(rhs);
-}
-
-const CtrlFlow = struct {
- c: *Context,
- ltoken: ast.TokenIndex,
- label_token: ?ast.TokenIndex,
- tag: ast.Node.Tag,
-
- /// Does everything except the RHS.
- fn init(c: *Context, tag: ast.Node.Tag, label: ?[]const u8) !CtrlFlow {
- const kw: Token.Id = switch (tag) {
- .Break => .Keyword_break,
- .Continue => .Keyword_continue,
- .Return => .Keyword_return,
- else => unreachable,
- };
- const kw_text = switch (tag) {
- .Break => "break",
- .Continue => "continue",
- .Return => "return",
- else => unreachable,
- };
- const ltoken = try appendToken(c, kw, kw_text);
- const label_token = if (label) |l| blk: {
- _ = try appendToken(c, .Colon, ":");
- break :blk try appendIdentifier(c, l);
- } else null;
- return CtrlFlow{
- .c = c,
- .ltoken = ltoken,
- .label_token = label_token,
- .tag = tag,
- };
- }
-
- fn initToken(c: *Context, tag: ast.Node.Tag, label: ?ast.TokenIndex) !CtrlFlow {
- const other_token = label orelse return init(c, tag, null);
- const loc = c.token_locs.items[other_token];
- const label_name = c.source_buffer.items[loc.start..loc.end];
- return init(c, tag, label_name);
- }
-
- fn finish(self: *CtrlFlow, rhs: ?*ast.Node) !*ast.Node.ControlFlowExpression {
- return ast.Node.ControlFlowExpression.create(self.c.arena, .{
- .ltoken = self.ltoken,
- .tag = self.tag,
- }, .{
- .label = self.label_token,
- .rhs = rhs,
- });
- }
-};
-
-fn transCreateNodeWhile(c: *Context) !*ast.Node.While {
- const while_tok = try appendToken(c, .Keyword_while, "while");
- _ = try appendToken(c, .LParen, "(");
-
- const node = try c.arena.create(ast.Node.While);
- node.* = .{
- .label = null,
- .inline_token = null,
- .while_token = while_tok,
- .condition = undefined,
- .payload = null,
- .continue_expr = null,
- .body = undefined,
- .@"else" = null,
- };
- return node;
-}
-
-fn transCreateNodeContinue(c: *Context) !*ast.Node {
- const ltoken = try appendToken(c, .Keyword_continue, "continue");
- const node = try ast.Node.ControlFlowExpression.create(c.arena, .{
- .ltoken = ltoken,
- .tag = .Continue,
- }, .{});
- _ = try appendToken(c, .Semicolon, ";");
- return &node.base;
-}
-
-fn transCreateNodeSwitchCase(c: *Context, lhs: *ast.Node) !*ast.Node.SwitchCase {
- const arrow_tok = try appendToken(c, .EqualAngleBracketRight, "=>");
-
- const node = try ast.Node.SwitchCase.alloc(c.arena, 1);
- node.* = .{
- .items_len = 1,
- .arrow_token = arrow_tok,
- .payload = null,
- .expr = undefined,
- };
- node.items()[0] = lhs;
- return node;
-}
-
-fn transCreateNodeSwitchElse(c: *Context) !*ast.Node {
- const node = try c.arena.create(ast.Node.SwitchElse);
- node.* = .{
- .token = try appendToken(c, .Keyword_else, "else"),
- };
- return &node.base;
-}
-
-fn transCreateNodeShiftOp(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangBinaryOperator,
- op: ast.Node.Tag,
- op_tok_id: std.zig.Token.Id,
- bytes: []const u8,
-) !*ast.Node {
- std.debug.assert(op == .BitShiftLeft or op == .BitShiftRight);
-
- const lhs_expr = ZigClangBinaryOperator_getLHS(stmt);
- const rhs_expr = ZigClangBinaryOperator_getRHS(stmt);
- const rhs_location = ZigClangExpr_getBeginLoc(rhs_expr);
- // lhs >> @as(u5, rh)
-
- const lhs = try transExpr(rp, scope, lhs_expr, .used, .l_value);
- const op_token = try appendToken(rp.c, op_tok_id, bytes);
-
- const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
- const rhs_type = try qualTypeToLog2IntRef(rp, ZigClangBinaryOperator_getType(stmt), rhs_location);
- cast_node.params()[0] = rhs_type;
- _ = try appendToken(rp.c, .Comma, ",");
- const rhs = try transExprCoercing(rp, scope, rhs_expr, .used, .r_value);
- cast_node.params()[1] = rhs;
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- const node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
- node.* = .{
- .base = .{ .tag = op },
- .op_token = op_token,
- .lhs = lhs,
- .rhs = &cast_node.base,
- };
-
- return &node.base;
-}
-
-fn transCreateNodePtrDeref(c: *Context, lhs: *ast.Node) !*ast.Node {
- const node = try c.arena.create(ast.Node.SimpleSuffixOp);
- node.* = .{
- .base = .{ .tag = .Deref },
- .lhs = lhs,
- .rtoken = try appendToken(c, .PeriodAsterisk, ".*"),
- };
- return &node.base;
-}
-
-fn transCreateNodeArrayAccess(c: *Context, lhs: *ast.Node) !*ast.Node.ArrayAccess {
- _ = try appendToken(c, .LBrace, "[");
- const node = try c.arena.create(ast.Node.ArrayAccess);
- node.* = .{
- .lhs = lhs,
- .index_expr = undefined,
- .rtoken = undefined,
- };
- return node;
-}
-
-const RestorePoint = struct {
- c: *Context,
- token_index: ast.TokenIndex,
- src_buf_index: usize,
-
- fn activate(self: RestorePoint) void {
- self.c.token_ids.shrink(self.c.gpa, self.token_index);
- self.c.token_locs.shrink(self.c.gpa, self.token_index);
- self.c.source_buffer.shrink(self.src_buf_index);
- }
-};
-
-fn makeRestorePoint(c: *Context) RestorePoint {
- return RestorePoint{
- .c = c,
- .token_index = c.token_ids.items.len,
- .src_buf_index = c.source_buffer.items.len,
- };
-}
-
-fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSourceLocation) TypeError!*ast.Node {
- switch (ZigClangType_getTypeClass(ty)) {
- .Builtin => {
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
- return transCreateNodeIdentifier(rp.c, switch (ZigClangBuiltinType_getKind(builtin_ty)) {
- .Void => "c_void",
- .Bool => "bool",
- .Char_U, .UChar, .Char_S, .Char8 => "u8",
- .SChar => "i8",
- .UShort => "c_ushort",
- .UInt => "c_uint",
- .ULong => "c_ulong",
- .ULongLong => "c_ulonglong",
- .Short => "c_short",
- .Int => "c_int",
- .Long => "c_long",
- .LongLong => "c_longlong",
- .UInt128 => "u128",
- .Int128 => "i128",
- .Float => "f32",
- .Double => "f64",
- .Float128 => "f128",
- .Float16 => "f16",
- .LongDouble => "c_longdouble",
- else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}),
- });
- },
- .FunctionProto => {
- const fn_proto_ty = @ptrCast(*const ZigClangFunctionProtoType, ty);
- const fn_proto = try transFnProto(rp, null, fn_proto_ty, source_loc, null, false);
- return &fn_proto.base;
- },
- .FunctionNoProto => {
- const fn_no_proto_ty = @ptrCast(*const ZigClangFunctionType, ty);
- const fn_proto = try transFnNoProto(rp, fn_no_proto_ty, source_loc, null, false);
- return &fn_proto.base;
- },
- .Paren => {
- const paren_ty = @ptrCast(*const ZigClangParenType, ty);
- return transQualType(rp, ZigClangParenType_getInnerType(paren_ty), source_loc);
- },
- .Pointer => {
- const child_qt = ZigClangType_getPointeeType(ty);
- if (qualTypeChildIsFnProto(child_qt)) {
- const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");
- optional_node.rhs = try transQualType(rp, child_qt, source_loc);
- return &optional_node.base;
- }
- if (typeIsOpaque(rp.c, ZigClangQualType_getTypePtr(child_qt), source_loc)) {
- const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");
- const pointer_node = try transCreateNodePtrType(
- rp.c,
- ZigClangQualType_isConstQualified(child_qt),
- ZigClangQualType_isVolatileQualified(child_qt),
- .Asterisk,
- );
- optional_node.rhs = &pointer_node.base;
- pointer_node.rhs = try transQualType(rp, child_qt, source_loc);
- return &optional_node.base;
- }
- const pointer_node = try transCreateNodePtrType(
- rp.c,
- ZigClangQualType_isConstQualified(child_qt),
- ZigClangQualType_isVolatileQualified(child_qt),
- .Identifier,
- );
- pointer_node.rhs = try transQualType(rp, child_qt, source_loc);
- return &pointer_node.base;
- },
- .ConstantArray => {
- const const_arr_ty = @ptrCast(*const ZigClangConstantArrayType, ty);
-
- const size_ap_int = ZigClangConstantArrayType_getSize(const_arr_ty);
- const size = ZigClangAPInt_getLimitedValue(size_ap_int, math.maxInt(usize));
- const elem_ty = ZigClangQualType_getTypePtr(ZigClangConstantArrayType_getElementType(const_arr_ty));
- return try transCreateNodeArrayType(rp, source_loc, elem_ty, size);
- },
- .IncompleteArray => {
- const incomplete_array_ty = @ptrCast(*const ZigClangIncompleteArrayType, ty);
-
- const child_qt = ZigClangIncompleteArrayType_getElementType(incomplete_array_ty);
- var node = try transCreateNodePtrType(
- rp.c,
- ZigClangQualType_isConstQualified(child_qt),
- ZigClangQualType_isVolatileQualified(child_qt),
- .Identifier,
- );
- node.rhs = try transQualType(rp, child_qt, source_loc);
- return &node.base;
- },
- .Typedef => {
- const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty);
-
- const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty);
- return (try transTypeDef(rp.c, typedef_decl, false)) orelse
- revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to translate typedef declaration", .{});
- },
- .Record => {
- const record_ty = @ptrCast(*const ZigClangRecordType, ty);
-
- const record_decl = ZigClangRecordType_getDecl(record_ty);
- return (try transRecordDecl(rp.c, record_decl)) orelse
- revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to resolve record declaration", .{});
- },
- .Enum => {
- const enum_ty = @ptrCast(*const ZigClangEnumType, ty);
-
- const enum_decl = ZigClangEnumType_getDecl(enum_ty);
- return (try transEnumDecl(rp.c, enum_decl)) orelse
- revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to translate enum declaration", .{});
- },
- .Elaborated => {
- const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ty);
- return transQualType(rp, ZigClangElaboratedType_getNamedType(elaborated_ty), source_loc);
- },
- .Decayed => {
- const decayed_ty = @ptrCast(*const ZigClangDecayedType, ty);
- return transQualType(rp, ZigClangDecayedType_getDecayedType(decayed_ty), source_loc);
- },
- .Attributed => {
- const attributed_ty = @ptrCast(*const ZigClangAttributedType, ty);
- return transQualType(rp, ZigClangAttributedType_getEquivalentType(attributed_ty), source_loc);
- },
- .MacroQualified => {
- const macroqualified_ty = @ptrCast(*const ZigClangMacroQualifiedType, ty);
- return transQualType(rp, ZigClangMacroQualifiedType_getModifiedType(macroqualified_ty), source_loc);
- },
- else => {
- const type_name = rp.c.str(ZigClangType_getTypeClassName(ty));
- return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported type: '{}'", .{type_name});
- },
- }
-}
-
-fn isCVoid(qt: ZigClangQualType) bool {
- const ty = ZigClangQualType_getTypePtr(qt);
- if (ZigClangType_getTypeClass(ty) == .Builtin) {
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
- return ZigClangBuiltinType_getKind(builtin_ty) == .Void;
- }
- return false;
-}
-
-const FnDeclContext = struct {
- fn_name: []const u8,
- has_body: bool,
- storage_class: ZigClangStorageClass,
- is_export: bool,
-};
-
-fn transCC(
- rp: RestorePoint,
- fn_ty: *const ZigClangFunctionType,
- source_loc: ZigClangSourceLocation,
-) !CallingConvention {
- const clang_cc = ZigClangFunctionType_getCallConv(fn_ty);
- switch (clang_cc) {
- .C => return CallingConvention.C,
- .X86StdCall => return CallingConvention.Stdcall,
- .X86FastCall => return CallingConvention.Fastcall,
- .X86VectorCall, .AArch64VectorCall => return CallingConvention.Vectorcall,
- .X86ThisCall => return CallingConvention.Thiscall,
- .AAPCS => return CallingConvention.AAPCS,
- .AAPCS_VFP => return CallingConvention.AAPCSVFP,
- else => return revertAndWarn(
- rp,
- error.UnsupportedType,
- source_loc,
- "unsupported calling convention: {}",
- .{@tagName(clang_cc)},
- ),
- }
-}
-
-fn transFnProto(
- rp: RestorePoint,
- fn_decl: ?*const ZigClangFunctionDecl,
- fn_proto_ty: *const ZigClangFunctionProtoType,
- source_loc: ZigClangSourceLocation,
- fn_decl_context: ?FnDeclContext,
- is_pub: bool,
-) !*ast.Node.FnProto {
- const fn_ty = @ptrCast(*const ZigClangFunctionType, fn_proto_ty);
- const cc = try transCC(rp, fn_ty, source_loc);
- const is_var_args = ZigClangFunctionProtoType_isVariadic(fn_proto_ty);
- return finishTransFnProto(rp, fn_decl, fn_proto_ty, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub);
-}
-
-fn transFnNoProto(
- rp: RestorePoint,
- fn_ty: *const ZigClangFunctionType,
- source_loc: ZigClangSourceLocation,
- fn_decl_context: ?FnDeclContext,
- is_pub: bool,
-) !*ast.Node.FnProto {
- const cc = try transCC(rp, fn_ty, source_loc);
- const is_var_args = if (fn_decl_context) |ctx| !ctx.is_export else true;
- return finishTransFnProto(rp, null, null, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub);
-}
-
-fn finishTransFnProto(
- rp: RestorePoint,
- fn_decl: ?*const ZigClangFunctionDecl,
- fn_proto_ty: ?*const ZigClangFunctionProtoType,
- fn_ty: *const ZigClangFunctionType,
- source_loc: ZigClangSourceLocation,
- fn_decl_context: ?FnDeclContext,
- is_var_args: bool,
- cc: CallingConvention,
- is_pub: bool,
-) !*ast.Node.FnProto {
- const is_export = if (fn_decl_context) |ctx| ctx.is_export else false;
- const is_extern = if (fn_decl_context) |ctx| !ctx.has_body else false;
-
- // TODO check for always_inline attribute
- // TODO check for align attribute
-
- // pub extern fn name(...) T
- const pub_tok = if (is_pub) try appendToken(rp.c, .Keyword_pub, "pub") else null;
- const extern_export_inline_tok = if (is_export)
- try appendToken(rp.c, .Keyword_export, "export")
- else if (is_extern)
- try appendToken(rp.c, .Keyword_extern, "extern")
- else
- null;
- const fn_tok = try appendToken(rp.c, .Keyword_fn, "fn");
- const name_tok = if (fn_decl_context) |ctx| try appendIdentifier(rp.c, ctx.fn_name) else null;
- const lparen_tok = try appendToken(rp.c, .LParen, "(");
-
- var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(rp.c.gpa);
- defer fn_params.deinit();
- const param_count: usize = if (fn_proto_ty != null) ZigClangFunctionProtoType_getNumParams(fn_proto_ty.?) else 0;
- try fn_params.ensureCapacity(param_count + 1); // +1 for possible var args node
-
- var i: usize = 0;
- while (i < param_count) : (i += 1) {
- const param_qt = ZigClangFunctionProtoType_getParamType(fn_proto_ty.?, @intCast(c_uint, i));
-
- const noalias_tok = if (ZigClangQualType_isRestrictQualified(param_qt)) try appendToken(rp.c, .Keyword_noalias, "noalias") else null;
-
- const param_name_tok: ?ast.TokenIndex = blk: {
- if (fn_decl) |decl| {
- const param = ZigClangFunctionDecl_getParamDecl(decl, @intCast(c_uint, i));
- const param_name: []const u8 = try rp.c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, param)));
- if (param_name.len < 1)
- break :blk null;
-
- const result = try appendIdentifier(rp.c, param_name);
- _ = try appendToken(rp.c, .Colon, ":");
- break :blk result;
- }
- break :blk null;
- };
-
- const type_node = try transQualType(rp, param_qt, source_loc);
-
- fn_params.addOneAssumeCapacity().* = .{
- .doc_comments = null,
- .comptime_token = null,
- .noalias_token = noalias_tok,
- .name_token = param_name_tok,
- .param_type = .{ .type_expr = type_node },
- };
-
- if (i + 1 < param_count) {
- _ = try appendToken(rp.c, .Comma, ",");
- }
- }
-
- const var_args_token: ?ast.TokenIndex = if (is_var_args) blk: {
- if (param_count > 0) {
- _ = try appendToken(rp.c, .Comma, ",");
- }
- break :blk try appendToken(rp.c, .Ellipsis3, "...");
- } else null;
-
- const rparen_tok = try appendToken(rp.c, .RParen, ")");
-
- const linksection_expr = blk: {
- if (fn_decl) |decl| {
- var str_len: usize = undefined;
- if (ZigClangFunctionDecl_getSectionAttribute(decl, &str_len)) |str_ptr| {
- _ = try appendToken(rp.c, .Keyword_linksection, "linksection");
- _ = try appendToken(rp.c, .LParen, "(");
- const expr = try transCreateNodeStringLiteral(
- rp.c,
- try std.fmt.allocPrint(rp.c.arena, "\"{}\"", .{str_ptr[0..str_len]}),
- );
- _ = try appendToken(rp.c, .RParen, ")");
-
- break :blk expr;
- }
- }
- break :blk null;
- };
-
- const align_expr = blk: {
- if (fn_decl) |decl| {
- const alignment = ZigClangFunctionDecl_getAlignedAttribute(decl, rp.c.clang_context);
- if (alignment != 0) {
- _ = try appendToken(rp.c, .Keyword_align, "align");
- _ = try appendToken(rp.c, .LParen, "(");
- // Clang reports the alignment in bits
- const expr = try transCreateNodeInt(rp.c, alignment / 8);
- _ = try appendToken(rp.c, .RParen, ")");
-
- break :blk expr;
- }
- }
- break :blk null;
- };
-
- const callconv_expr = if ((is_export or is_extern) and cc == .C) null else blk: {
- _ = try appendToken(rp.c, .Keyword_callconv, "callconv");
- _ = try appendToken(rp.c, .LParen, "(");
- const expr = try transCreateNodeEnumLiteral(rp.c, @tagName(cc));
- _ = try appendToken(rp.c, .RParen, ")");
- break :blk expr;
- };
-
- const return_type_node = blk: {
- if (ZigClangFunctionType_getNoReturnAttr(fn_ty)) {
- break :blk try transCreateNodeIdentifier(rp.c, "noreturn");
- } else {
- const return_qt = ZigClangFunctionType_getReturnType(fn_ty);
- if (isCVoid(return_qt)) {
- // convert primitive c_void to actual void (only for return type)
- break :blk try transCreateNodeIdentifier(rp.c, "void");
- } else {
- break :blk transQualType(rp, return_qt, source_loc) catch |err| switch (err) {
- error.UnsupportedType => {
- try emitWarning(rp.c, source_loc, "unsupported function proto return type", .{});
- return err;
- },
- error.OutOfMemory => |e| return e,
- };
- }
- }
- };
-
- // We need to reserve an undefined (but non-null) body node to set later.
- var body_node: ?*ast.Node = null;
- if (fn_decl_context) |ctx| {
- if (ctx.has_body) {
- // TODO: we should be able to use undefined here but
- // it causes a bug. This is undefined without zig language
- // being aware of it.
- body_node = @intToPtr(*ast.Node, 0x08);
- }
- }
-
- const fn_proto = try ast.Node.FnProto.create(rp.c.arena, .{
- .params_len = fn_params.items.len,
- .return_type = .{ .Explicit = return_type_node },
- .fn_token = fn_tok,
- }, .{
- .visib_token = pub_tok,
- .name_token = name_tok,
- .extern_export_inline_token = extern_export_inline_tok,
- .align_expr = align_expr,
- .section_expr = linksection_expr,
- .callconv_expr = callconv_expr,
- .body_node = body_node,
- .var_args_token = var_args_token,
- });
- mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
- return fn_proto;
-}
-
-fn revertAndWarn(
- rp: RestorePoint,
- err: anytype,
- source_loc: ZigClangSourceLocation,
- comptime format: []const u8,
- args: anytype,
-) (@TypeOf(err) || error{OutOfMemory}) {
- rp.activate();
- try emitWarning(rp.c, source_loc, format, args);
- return err;
-}
-
-fn emitWarning(c: *Context, loc: ZigClangSourceLocation, comptime format: []const u8, args: anytype) !void {
- const args_prefix = .{c.locStr(loc)};
- _ = try appendTokenFmt(c, .LineComment, "// {}: warning: " ++ format, args_prefix ++ args);
-}
-
-pub fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comptime format: []const u8, args: anytype) !void {
- // pub const name = @compileError(msg);
- const pub_tok = try appendToken(c, .Keyword_pub, "pub");
- const const_tok = try appendToken(c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(c, name);
- const eq_tok = try appendToken(c, .Equal, "=");
- const builtin_tok = try appendToken(c, .Builtin, "@compileError");
- const lparen_tok = try appendToken(c, .LParen, "(");
- const msg_tok = try appendTokenFmt(c, .StringLiteral, "\"" ++ format ++ "\"", args);
- const rparen_tok = try appendToken(c, .RParen, ")");
- const semi_tok = try appendToken(c, .Semicolon, ";");
- _ = try appendTokenFmt(c, .LineComment, "// {}", .{c.locStr(loc)});
-
- const msg_node = try c.arena.create(ast.Node.OneToken);
- msg_node.* = .{
- .base = .{ .tag = .StringLiteral },
- .token = msg_tok,
- };
-
- const call_node = try ast.Node.BuiltinCall.alloc(c.arena, 1);
- call_node.* = .{
- .builtin_token = builtin_tok,
- .params_len = 1,
- .rparen_token = rparen_tok,
- };
- call_node.params()[0] = &msg_node.base;
-
- const var_decl_node = try ast.Node.VarDecl.create(c.arena, .{
- .name_token = name_tok,
- .mut_token = const_tok,
- .semicolon_token = semi_tok,
- }, .{
- .visib_token = pub_tok,
- .eq_token = eq_tok,
- .init_node = &call_node.base,
- });
- try addTopLevelDecl(c, name, &var_decl_node.base);
-}
-
-fn appendToken(c: *Context, token_id: Token.Id, bytes: []const u8) !ast.TokenIndex {
- std.debug.assert(token_id != .Identifier); // use appendIdentifier
- return appendTokenFmt(c, token_id, "{}", .{bytes});
-}
-
-fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, args: anytype) !ast.TokenIndex {
- assert(token_id != .Invalid);
-
- try c.token_ids.ensureCapacity(c.gpa, c.token_ids.items.len + 1);
- try c.token_locs.ensureCapacity(c.gpa, c.token_locs.items.len + 1);
-
- const start_index = c.source_buffer.items.len;
- try c.source_buffer.outStream().print(format ++ " ", args);
-
- c.token_ids.appendAssumeCapacity(token_id);
- c.token_locs.appendAssumeCapacity(.{
- .start = start_index,
- .end = c.source_buffer.items.len - 1, // back up before the space
- });
-
- return c.token_ids.items.len - 1;
-}
-
-// TODO hook up with codegen
-fn isZigPrimitiveType(name: []const u8) bool {
- if (name.len > 1 and (name[0] == 'u' or name[0] == 'i')) {
- for (name[1..]) |c| {
- switch (c) {
- '0'...'9' => {},
- else => return false,
- }
- }
- return true;
- }
- // void is invalid in c so it doesn't need to be checked.
- return mem.eql(u8, name, "comptime_float") or
- mem.eql(u8, name, "comptime_int") or
- mem.eql(u8, name, "bool") or
- mem.eql(u8, name, "isize") or
- mem.eql(u8, name, "usize") or
- mem.eql(u8, name, "f16") or
- mem.eql(u8, name, "f32") or
- mem.eql(u8, name, "f64") or
- mem.eql(u8, name, "f128") or
- mem.eql(u8, name, "c_longdouble") or
- mem.eql(u8, name, "noreturn") or
- mem.eql(u8, name, "type") or
- mem.eql(u8, name, "anyerror") or
- mem.eql(u8, name, "c_short") or
- mem.eql(u8, name, "c_ushort") or
- mem.eql(u8, name, "c_int") or
- mem.eql(u8, name, "c_uint") or
- mem.eql(u8, name, "c_long") or
- mem.eql(u8, name, "c_ulong") or
- mem.eql(u8, name, "c_longlong") or
- mem.eql(u8, name, "c_ulonglong");
-}
-
-fn isValidZigIdentifier(name: []const u8) bool {
- for (name) |c, i| {
- switch (c) {
- '_', 'a'...'z', 'A'...'Z' => {},
- '0'...'9' => if (i == 0) return false,
- else => return false,
- }
- }
- return true;
-}
-
-fn appendIdentifier(c: *Context, name: []const u8) !ast.TokenIndex {
- if (!isValidZigIdentifier(name) or std.zig.Token.getKeyword(name) != null) {
- return appendTokenFmt(c, .Identifier, "@\"{}\"", .{name});
- } else {
- return appendTokenFmt(c, .Identifier, "{}", .{name});
- }
-}
-
-fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node {
- const token_index = try appendIdentifier(c, name);
- const identifier = try c.arena.create(ast.Node.OneToken);
- identifier.* = .{
- .base = .{ .tag = .Identifier },
- .token = token_index,
- };
- return &identifier.base;
-}
-
-fn transCreateNodeIdentifierUnchecked(c: *Context, name: []const u8) !*ast.Node {
- const token_index = try appendTokenFmt(c, .Identifier, "{}", .{name});
- const identifier = try c.arena.create(ast.Node.OneToken);
- identifier.* = .{
- .base = .{ .tag = .Identifier },
- .token = token_index,
- };
- return &identifier.base;
-}
-
-pub fn freeErrors(errors: []ClangErrMsg) void {
- ZigClangErrorMsg_delete(errors.ptr, errors.len);
-}
-
-const MacroCtx = struct {
- source: []const u8,
- list: []const CToken,
- i: usize = 0,
- loc: ZigClangSourceLocation,
- name: []const u8,
-
- fn peek(self: *MacroCtx) ?CToken.Id {
- if (self.i >= self.list.len) return null;
- return self.list[self.i + 1].id;
- }
-
- fn next(self: *MacroCtx) ?CToken.Id {
- if (self.i >= self.list.len) return null;
- self.i += 1;
- return self.list[self.i].id;
- }
-
- fn slice(self: *MacroCtx) []const u8 {
- const tok = self.list[self.i];
- return self.source[tok.start..tok.end];
- }
-
- fn fail(self: *MacroCtx, c: *Context, comptime fmt: []const u8, args: anytype) !void {
- return failDecl(c, self.loc, self.name, fmt, args);
- }
-};
-
-fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
- // TODO if we see #undef, delete it from the table
- var it = ZigClangASTUnit_getLocalPreprocessingEntities_begin(unit);
- const it_end = ZigClangASTUnit_getLocalPreprocessingEntities_end(unit);
- var tok_list = std.ArrayList(CToken).init(c.gpa);
- defer tok_list.deinit();
- const scope = c.global_scope;
-
- while (it.I != it_end.I) : (it.I += 1) {
- const entity = ZigClangPreprocessingRecord_iterator_deref(it);
- tok_list.items.len = 0;
- switch (ZigClangPreprocessedEntity_getKind(entity)) {
- .MacroDefinitionKind => {
- const macro = @ptrCast(*ZigClangMacroDefinitionRecord, entity);
- const raw_name = ZigClangMacroDefinitionRecord_getName_getNameStart(macro);
- const begin_loc = ZigClangMacroDefinitionRecord_getSourceRange_getBegin(macro);
-
- const name = try c.str(raw_name);
- // TODO https://github.com/ziglang/zig/issues/3756
- // TODO https://github.com/ziglang/zig/issues/1802
- const mangled_name = if (isZigPrimitiveType(name)) try std.fmt.allocPrint(c.arena, "{}_{}", .{ name, c.getMangle() }) else name;
- if (scope.containsNow(mangled_name)) {
- continue;
- }
-
- const begin_c = ZigClangSourceManager_getCharacterData(c.source_manager, begin_loc);
- const slice = begin_c[0..mem.len(begin_c)];
-
- var tokenizer = std.c.Tokenizer{
- .buffer = slice,
- };
- while (true) {
- const tok = tokenizer.next();
- switch (tok.id) {
- .Nl, .Eof => {
- try tok_list.append(tok);
- break;
- },
- .LineComment, .MultiLineComment => continue,
- else => {},
- }
- try tok_list.append(tok);
- }
-
- var macro_ctx = MacroCtx{
- .source = slice,
- .list = tok_list.items,
- .name = mangled_name,
- .loc = begin_loc,
- };
- assert(mem.eql(u8, macro_ctx.slice(), name));
-
- var macro_fn = false;
- switch (macro_ctx.peek().?) {
- .Identifier => {
- // if it equals itself, ignore. for example, from stdio.h:
- // #define stdin stdin
- const tok = macro_ctx.list[1];
- if (mem.eql(u8, name, slice[tok.start..tok.end])) {
- continue;
- }
- },
- .Nl, .Eof => {
- // this means it is a macro without a value
- // we don't care about such things
- continue;
- },
- .LParen => {
- // if the name is immediately followed by a '(' then it is a function
- macro_fn = macro_ctx.list[0].end == macro_ctx.list[1].start;
- },
- else => {},
- }
-
- (if (macro_fn)
- transMacroFnDefine(c, ¯o_ctx)
- else
- transMacroDefine(c, ¯o_ctx)) catch |err| switch (err) {
- error.ParseError => continue,
- error.OutOfMemory => |e| return e,
- };
- },
- else => {},
- }
- }
-}
-
-fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
- const scope = &c.global_scope.base;
-
- const visib_tok = try appendToken(c, .Keyword_pub, "pub");
- const mut_tok = try appendToken(c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(c, m.name);
- const eq_token = try appendToken(c, .Equal, "=");
-
- const init_node = try parseCExpr(c, m, scope);
- const last = m.next().?;
- if (last != .Eof and last != .Nl)
- return m.fail(c, "unable to translate C expr: unexpected token .{}", .{@tagName(last)});
-
- const semicolon_token = try appendToken(c, .Semicolon, ";");
- const node = try ast.Node.VarDecl.create(c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .visib_token = visib_tok,
- .eq_token = eq_token,
- .init_node = init_node,
- });
- _ = try c.global_scope.macro_table.put(m.name, &node.base);
-}
-
-fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
- var block_scope = try Scope.Block.init(c, &c.global_scope.base, false);
- defer block_scope.deinit();
- const scope = &block_scope.base;
-
- const pub_tok = try appendToken(c, .Keyword_pub, "pub");
- const inline_tok = try appendToken(c, .Keyword_inline, "inline");
- const fn_tok = try appendToken(c, .Keyword_fn, "fn");
- const name_tok = try appendIdentifier(c, m.name);
- _ = try appendToken(c, .LParen, "(");
-
- if (m.next().? != .LParen) {
- return m.fail(c, "unable to translate C expr: expected '('", .{});
- }
-
- var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(c.gpa);
- defer fn_params.deinit();
-
- while (true) {
- if (m.next().? != .Identifier) {
- return m.fail(c, "unable to translate C expr: expected identifier", .{});
- }
-
- const mangled_name = try block_scope.makeMangledName(c, m.slice());
- const param_name_tok = try appendIdentifier(c, mangled_name);
- _ = try appendToken(c, .Colon, ":");
-
- const any_type = try c.arena.create(ast.Node.OneToken);
- any_type.* = .{
- .base = .{ .tag = .AnyType },
- .token = try appendToken(c, .Keyword_anytype, "anytype"),
- };
-
- (try fn_params.addOne()).* = .{
- .doc_comments = null,
- .comptime_token = null,
- .noalias_token = null,
- .name_token = param_name_tok,
- .param_type = .{ .any_type = &any_type.base },
- };
-
- if (m.peek().? != .Comma)
- break;
- _ = m.next();
- _ = try appendToken(c, .Comma, ",");
- }
-
- if (m.next().? != .RParen) {
- return m.fail(c, "unable to translate C expr: expected ')'", .{});
- }
-
- _ = try appendToken(c, .RParen, ")");
-
- const type_of = try c.createBuiltinCall("@TypeOf", 1);
-
- const return_kw = try appendToken(c, .Keyword_return, "return");
- const expr = try parseCExpr(c, m, scope);
- const last = m.next().?;
- if (last != .Eof and last != .Nl)
- return m.fail(c, "unable to translate C expr: unexpected token .{}", .{@tagName(last)});
- _ = try appendToken(c, .Semicolon, ";");
- const type_of_arg = if (!expr.tag.isBlock()) expr else blk: {
- const stmts = expr.blockStatements();
- const blk_last = stmts[stmts.len - 1];
- const br = blk_last.cast(ast.Node.ControlFlowExpression).?;
- break :blk br.getRHS().?;
- };
- type_of.params()[0] = type_of_arg;
- type_of.rparen_token = try appendToken(c, .RParen, ")");
- const return_expr = try ast.Node.ControlFlowExpression.create(c.arena, .{
- .ltoken = return_kw,
- .tag = .Return,
- }, .{
- .rhs = expr,
- });
-
- try block_scope.statements.append(&return_expr.base);
- const block_node = try block_scope.complete(c);
- const fn_proto = try ast.Node.FnProto.create(c.arena, .{
- .fn_token = fn_tok,
- .params_len = fn_params.items.len,
- .return_type = .{ .Explicit = &type_of.base },
- }, .{
- .visib_token = pub_tok,
- .extern_export_inline_token = inline_tok,
- .name_token = name_tok,
- .body_node = block_node,
- });
- mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
-
- _ = try c.global_scope.macro_table.put(m.name, &fn_proto.base);
-}
-
-const ParseError = Error || error{ParseError};
-
-fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
- const node = try parseCPrefixOpExpr(c, m, scope);
- switch (m.next().?) {
- .QuestionMark => {
- // must come immediately after expr
- _ = try appendToken(c, .RParen, ")");
- const if_node = try transCreateNodeIf(c);
- if_node.condition = node;
- if_node.body = try parseCPrimaryExpr(c, m, scope);
- if (m.next().? != .Colon) {
- try m.fail(c, "unable to translate C expr: expected ':'", .{});
- return error.ParseError;
- }
- if_node.@"else" = try transCreateNodeElse(c);
- if_node.@"else".?.body = try parseCPrimaryExpr(c, m, scope);
- return &if_node.base;
- },
- .Comma => {
- _ = try appendToken(c, .Semicolon, ";");
- var block_scope = try Scope.Block.init(c, scope, true);
- defer block_scope.deinit();
-
- var last = node;
- while (true) {
- // suppress result
- const lhs = try transCreateNodeIdentifier(c, "_");
- const op_token = try appendToken(c, .Equal, "=");
- const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
- op_node.* = .{
- .base = .{ .tag = .Assign },
- .op_token = op_token,
- .lhs = lhs,
- .rhs = last,
- };
- try block_scope.statements.append(&op_node.base);
-
- last = try parseCPrefixOpExpr(c, m, scope);
- _ = try appendToken(c, .Semicolon, ";");
- if (m.next().? != .Comma) {
- m.i -= 1;
- break;
- }
- }
-
- const break_node = try transCreateNodeBreak(c, block_scope.label, last);
- try block_scope.statements.append(&break_node.base);
- return try block_scope.complete(c);
- },
- else => {
- m.i -= 1;
- return node;
- },
- }
-}
-
-fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node {
- var lit_bytes = m.slice();
-
- switch (m.list[m.i].id) {
- .IntegerLiteral => |suffix| {
- if (lit_bytes.len > 2 and lit_bytes[0] == '0') {
- switch (lit_bytes[1]) {
- '0'...'7' => {
- // Octal
- lit_bytes = try std.fmt.allocPrint(c.arena, "0o{}", .{lit_bytes});
- },
- 'X' => {
- // Hexadecimal with capital X, valid in C but not in Zig
- lit_bytes = try std.fmt.allocPrint(c.arena, "0x{}", .{lit_bytes[2..]});
- },
- else => {},
- }
- }
-
- if (suffix == .none) {
- return transCreateNodeInt(c, lit_bytes);
- }
-
- const cast_node = try c.createBuiltinCall("@as", 2);
- cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (suffix) {
- .u => "c_uint",
- .l => "c_long",
- .lu => "c_ulong",
- .ll => "c_longlong",
- .llu => "c_ulonglong",
- else => unreachable,
- });
- lit_bytes = lit_bytes[0 .. lit_bytes.len - switch (suffix) {
- .u, .l => @as(u8, 1),
- .lu, .ll => 2,
- .llu => 3,
- else => unreachable,
- }];
- _ = try appendToken(c, .Comma, ",");
- cast_node.params()[1] = try transCreateNodeInt(c, lit_bytes);
- cast_node.rparen_token = try appendToken(c, .RParen, ")");
- return &cast_node.base;
- },
- .FloatLiteral => |suffix| {
- if (lit_bytes[0] == '.')
- lit_bytes = try std.fmt.allocPrint(c.arena, "0{}", .{lit_bytes});
- if (suffix == .none) {
- return transCreateNodeFloat(c, lit_bytes);
- }
- const cast_node = try c.createBuiltinCall("@as", 2);
- cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (suffix) {
- .f => "f32",
- .l => "c_longdouble",
- else => unreachable,
- });
- _ = try appendToken(c, .Comma, ",");
- cast_node.params()[1] = try transCreateNodeFloat(c, lit_bytes[0 .. lit_bytes.len - 1]);
- cast_node.rparen_token = try appendToken(c, .RParen, ")");
- return &cast_node.base;
- },
- else => unreachable,
- }
-}
-
-fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
- var source = m.slice();
- for (source) |c, i| {
- if (c == '\"' or c == '\'') {
- source = source[i..];
- break;
- }
- }
- for (source) |c| {
- if (c == '\\') {
- break;
- }
- } else return source;
- var bytes = try ctx.arena.alloc(u8, source.len * 2);
- var state: enum {
- Start,
- Escape,
- Hex,
- Octal,
- } = .Start;
- var i: usize = 0;
- var count: u8 = 0;
- var num: u8 = 0;
- for (source) |c| {
- switch (state) {
- .Escape => {
- switch (c) {
- 'n', 'r', 't', '\\', '\'', '\"' => {
- bytes[i] = c;
- },
- '0'...'7' => {
- count += 1;
- num += c - '0';
- state = .Octal;
- bytes[i] = 'x';
- },
- 'x' => {
- state = .Hex;
- bytes[i] = 'x';
- },
- 'a' => {
- bytes[i] = 'x';
- i += 1;
- bytes[i] = '0';
- i += 1;
- bytes[i] = '7';
- },
- 'b' => {
- bytes[i] = 'x';
- i += 1;
- bytes[i] = '0';
- i += 1;
- bytes[i] = '8';
- },
- 'f' => {
- bytes[i] = 'x';
- i += 1;
- bytes[i] = '0';
- i += 1;
- bytes[i] = 'C';
- },
- 'v' => {
- bytes[i] = 'x';
- i += 1;
- bytes[i] = '0';
- i += 1;
- bytes[i] = 'B';
- },
- '?' => {
- i -= 1;
- bytes[i] = '?';
- },
- 'u', 'U' => {
- try m.fail(ctx, "macro tokenizing failed: TODO unicode escape sequences", .{});
- return error.ParseError;
- },
- else => {
- try m.fail(ctx, "macro tokenizing failed: unknown escape sequence", .{});
- return error.ParseError;
- },
- }
- i += 1;
- if (state == .Escape)
- state = .Start;
- },
- .Start => {
- if (c == '\\') {
- state = .Escape;
- }
- bytes[i] = c;
- i += 1;
- },
- .Hex => {
- switch (c) {
- '0'...'9' => {
- num = std.math.mul(u8, num, 16) catch {
- try m.fail(ctx, "macro tokenizing failed: hex literal overflowed", .{});
- return error.ParseError;
- };
- num += c - '0';
- },
- 'a'...'f' => {
- num = std.math.mul(u8, num, 16) catch {
- try m.fail(ctx, "macro tokenizing failed: hex literal overflowed", .{});
- return error.ParseError;
- };
- num += c - 'a' + 10;
- },
- 'A'...'F' => {
- num = std.math.mul(u8, num, 16) catch {
- try m.fail(ctx, "macro tokenizing failed: hex literal overflowed", .{});
- return error.ParseError;
- };
- num += c - 'A' + 10;
- },
- else => {
- i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{ .fill = '0', .width = 2 });
- num = 0;
- if (c == '\\')
- state = .Escape
- else
- state = .Start;
- bytes[i] = c;
- i += 1;
- },
- }
- },
- .Octal => {
- const accept_digit = switch (c) {
- // The maximum length of a octal literal is 3 digits
- '0'...'7' => count < 3,
- else => false,
- };
-
- if (accept_digit) {
- count += 1;
- num = std.math.mul(u8, num, 8) catch {
- try m.fail(ctx, "macro tokenizing failed: octal literal overflowed", .{});
- return error.ParseError;
- };
- num += c - '0';
- } else {
- i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{ .fill = '0', .width = 2 });
- num = 0;
- count = 0;
- if (c == '\\')
- state = .Escape
- else
- state = .Start;
- bytes[i] = c;
- i += 1;
- }
- },
- }
- }
- if (state == .Hex or state == .Octal)
- i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{ .fill = '0', .width = 2 });
- return bytes[0..i];
-}
-
-fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
- const tok = m.next().?;
- const slice = m.slice();
- switch (tok) {
- .CharLiteral => {
- if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) {
- const token = try appendToken(c, .CharLiteral, try zigifyEscapeSequences(c, m));
- const node = try c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .CharLiteral },
- .token = token,
- };
- return &node.base;
- } else {
- const token = try appendTokenFmt(c, .IntegerLiteral, "0x{x}", .{slice[1 .. slice.len - 1]});
- const node = try c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .IntegerLiteral },
- .token = token,
- };
- return &node.base;
- }
- },
- .StringLiteral => {
- const token = try appendToken(c, .StringLiteral, try zigifyEscapeSequences(c, m));
- const node = try c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .StringLiteral },
- .token = token,
- };
- return &node.base;
- },
- .IntegerLiteral, .FloatLiteral => {
- return parseCNumLit(c, m);
- },
- // eventually this will be replaced by std.c.parse which will handle these correctly
- .Keyword_void => return transCreateNodeIdentifierUnchecked(c, "c_void"),
- .Keyword_bool => return transCreateNodeIdentifierUnchecked(c, "bool"),
- .Keyword_double => return transCreateNodeIdentifierUnchecked(c, "f64"),
- .Keyword_long => return transCreateNodeIdentifierUnchecked(c, "c_long"),
- .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"),
- .Keyword_float => return transCreateNodeIdentifierUnchecked(c, "f32"),
- .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),
- .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"),
- .Keyword_unsigned => if (m.next()) |t| switch (t) {
- .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"),
- .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_ushort"),
- .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_uint"),
- .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) {
- _ = m.next();
- return transCreateNodeIdentifierUnchecked(c, "c_ulonglong");
- } else return transCreateNodeIdentifierUnchecked(c, "c_ulong"),
- else => {
- m.i -= 1;
- return transCreateNodeIdentifierUnchecked(c, "c_uint");
- },
- } else {
- return transCreateNodeIdentifierUnchecked(c, "c_uint");
- },
- .Keyword_signed => if (m.next()) |t| switch (t) {
- .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "i8"),
- .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),
- .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"),
- .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) {
- _ = m.next();
- return transCreateNodeIdentifierUnchecked(c, "c_longlong");
- } else return transCreateNodeIdentifierUnchecked(c, "c_long"),
- else => {
- m.i -= 1;
- return transCreateNodeIdentifierUnchecked(c, "c_int");
- },
- } else {
- return transCreateNodeIdentifierUnchecked(c, "c_int");
- },
- .Identifier => {
- const mangled_name = scope.getAlias(slice);
- return transCreateNodeIdentifier(c, checkForBuiltinTypedef(mangled_name) orelse mangled_name);
- },
- .LParen => {
- const inner_node = try parseCExpr(c, m, scope);
-
- const next_id = m.next().?;
- if (next_id != .RParen) {
- try m.fail(c, "unable to translate C expr: expected ')'' instead got: {}", .{@tagName(next_id)});
- return error.ParseError;
- }
- var saw_l_paren = false;
- var saw_integer_literal = false;
- switch (m.peek().?) {
- // (type)(to_cast)
- .LParen => {
- saw_l_paren = true;
- _ = m.next();
- },
- // (type)sizeof(x)
- .Keyword_sizeof,
- // (type)alignof(x)
- .Keyword_alignof,
- // (type)identifier
- .Identifier => {},
- // (type)integer
- .IntegerLiteral => {
- saw_integer_literal = true;
- },
- else => return inner_node,
- }
-
- // hack to get zig fmt to render a comma in builtin calls
- _ = try appendToken(c, .Comma, ",");
-
- const node_to_cast = try parseCExpr(c, m, scope);
-
- if (saw_l_paren and m.next().? != .RParen) {
- try m.fail(c, "unable to translate C expr: expected ')''", .{});
- return error.ParseError;
- }
-
- const lparen = try appendToken(c, .LParen, "(");
-
- //(@import("std").meta.cast(dest, x))
- const import_fn_call = try c.createBuiltinCall("@import", 1);
- const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
- import_fn_call.params()[0] = std_node;
- import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
- const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "meta");
- const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "cast");
-
- const cast_fn_call = try c.createCall(outer_field_access, 2);
- cast_fn_call.params()[0] = inner_node;
- cast_fn_call.params()[1] = node_to_cast;
- cast_fn_call.rtoken = try appendToken(c, .RParen, ")");
-
- const group_node = try c.arena.create(ast.Node.GroupedExpression);
- group_node.* = .{
- .lparen = lparen,
- .expr = &cast_fn_call.base,
- .rparen = try appendToken(c, .RParen, ")"),
- };
- return &group_node.base;
- },
- else => {
- try m.fail(c, "unable to translate C expr: unexpected token .{}", .{@tagName(tok)});
- return error.ParseError;
- },
- }
-}
-
-fn nodeIsInfixOp(tag: ast.Node.Tag) bool {
- return switch (tag) {
- .Add,
- .AddWrap,
- .ArrayCat,
- .ArrayMult,
- .Assign,
- .AssignBitAnd,
- .AssignBitOr,
- .AssignBitShiftLeft,
- .AssignBitShiftRight,
- .AssignBitXor,
- .AssignDiv,
- .AssignSub,
- .AssignSubWrap,
- .AssignMod,
- .AssignAdd,
- .AssignAddWrap,
- .AssignMul,
- .AssignMulWrap,
- .BangEqual,
- .BitAnd,
- .BitOr,
- .BitShiftLeft,
- .BitShiftRight,
- .BitXor,
- .BoolAnd,
- .BoolOr,
- .Div,
- .EqualEqual,
- .ErrorUnion,
- .GreaterOrEqual,
- .GreaterThan,
- .LessOrEqual,
- .LessThan,
- .MergeErrorSets,
- .Mod,
- .Mul,
- .MulWrap,
- .Period,
- .Range,
- .Sub,
- .SubWrap,
- .UnwrapOptional,
- .Catch,
- => true,
-
- else => false,
- };
-}
-
-fn macroBoolToInt(c: *Context, node: *ast.Node) !*ast.Node {
- if (!isBoolRes(node)) {
- if (!nodeIsInfixOp(node.tag)) return node;
-
- const group_node = try c.arena.create(ast.Node.GroupedExpression);
- group_node.* = .{
- .lparen = try appendToken(c, .LParen, "("),
- .expr = node,
- .rparen = try appendToken(c, .RParen, ")"),
- };
- return &group_node.base;
- }
-
- const builtin_node = try c.createBuiltinCall("@boolToInt", 1);
- builtin_node.params()[0] = node;
- builtin_node.rparen_token = try appendToken(c, .RParen, ")");
- return &builtin_node.base;
-}
-
-fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node {
- if (isBoolRes(node)) {
- if (!nodeIsInfixOp(node.tag)) return node;
-
- const group_node = try c.arena.create(ast.Node.GroupedExpression);
- group_node.* = .{
- .lparen = try appendToken(c, .LParen, "("),
- .expr = node,
- .rparen = try appendToken(c, .RParen, ")"),
- };
- return &group_node.base;
- }
-
- const op_token = try appendToken(c, .BangEqual, "!=");
- const zero = try transCreateNodeInt(c, 0);
- const res = try c.arena.create(ast.Node.SimpleInfixOp);
- res.* = .{
- .base = .{ .tag = .BangEqual },
- .op_token = op_token,
- .lhs = node,
- .rhs = zero,
- };
- const group_node = try c.arena.create(ast.Node.GroupedExpression);
- group_node.* = .{
- .lparen = try appendToken(c, .LParen, "("),
- .expr = &res.base,
- .rparen = try appendToken(c, .RParen, ")"),
- };
- return &group_node.base;
-}
-
-fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
- var node = try parseCPrimaryExpr(c, m, scope);
- while (true) {
- var op_token: ast.TokenIndex = undefined;
- var op_id: ast.Node.Tag = undefined;
- var bool_op = false;
- switch (m.next().?) {
- .Period => {
- if (m.next().? != .Identifier) {
- try m.fail(c, "unable to translate C expr: expected identifier", .{});
- return error.ParseError;
- }
-
- node = try transCreateNodeFieldAccess(c, node, m.slice());
- continue;
- },
- .Arrow => {
- if (m.next().? != .Identifier) {
- try m.fail(c, "unable to translate C expr: expected identifier", .{});
- return error.ParseError;
- }
- const deref = try transCreateNodePtrDeref(c, node);
- node = try transCreateNodeFieldAccess(c, deref, m.slice());
- continue;
- },
- .Asterisk => {
- if (m.peek().? == .RParen) {
- // type *)
-
- // hack to get zig fmt to render a comma in builtin calls
- _ = try appendToken(c, .Comma, ",");
-
- // last token of `node`
- const prev_id = m.list[m.i - 1].id;
-
- if (prev_id == .Keyword_void) {
- const ptr = try transCreateNodePtrType(c, false, false, .Asterisk);
- ptr.rhs = node;
- const optional_node = try transCreateNodeSimplePrefixOp(c, .OptionalType, .QuestionMark, "?");
- optional_node.rhs = &ptr.base;
- return &optional_node.base;
- } else {
- const ptr = try transCreateNodePtrType(c, false, false, Token.Id.Identifier);
- ptr.rhs = node;
- return &ptr.base;
- }
- } else {
- // expr * expr
- op_token = try appendToken(c, .Asterisk, "*");
- op_id = .BitShiftLeft;
- }
- },
- .AngleBracketAngleBracketLeft => {
- op_token = try appendToken(c, .AngleBracketAngleBracketLeft, "<<");
- op_id = .BitShiftLeft;
- },
- .AngleBracketAngleBracketRight => {
- op_token = try appendToken(c, .AngleBracketAngleBracketRight, ">>");
- op_id = .BitShiftRight;
- },
- .Pipe => {
- op_token = try appendToken(c, .Pipe, "|");
- op_id = .BitOr;
- },
- .Ampersand => {
- op_token = try appendToken(c, .Ampersand, "&");
- op_id = .BitAnd;
- },
- .Plus => {
- op_token = try appendToken(c, .Plus, "+");
- op_id = .Add;
- },
- .Minus => {
- op_token = try appendToken(c, .Minus, "-");
- op_id = .Sub;
- },
- .AmpersandAmpersand => {
- op_token = try appendToken(c, .Keyword_and, "and");
- op_id = .BoolAnd;
- bool_op = true;
- },
- .PipePipe => {
- op_token = try appendToken(c, .Keyword_or, "or");
- op_id = .BoolOr;
- bool_op = true;
- },
- .AngleBracketRight => {
- op_token = try appendToken(c, .AngleBracketRight, ">");
- op_id = .GreaterThan;
- },
- .AngleBracketRightEqual => {
- op_token = try appendToken(c, .AngleBracketRightEqual, ">=");
- op_id = .GreaterOrEqual;
- },
- .AngleBracketLeft => {
- op_token = try appendToken(c, .AngleBracketLeft, "<");
- op_id = .LessThan;
- },
- .AngleBracketLeftEqual => {
- op_token = try appendToken(c, .AngleBracketLeftEqual, "<=");
- op_id = .LessOrEqual;
- },
- .LBracket => {
- const arr_node = try transCreateNodeArrayAccess(c, node);
- arr_node.index_expr = try parseCPrefixOpExpr(c, m, scope);
- arr_node.rtoken = try appendToken(c, .RBracket, "]");
- node = &arr_node.base;
- if (m.next().? != .RBracket) {
- try m.fail(c, "unable to translate C expr: expected ']'", .{});
- return error.ParseError;
- }
- continue;
- },
- .LParen => {
- _ = try appendToken(c, .LParen, "(");
- var call_params = std.ArrayList(*ast.Node).init(c.gpa);
- defer call_params.deinit();
- while (true) {
- const arg = try parseCPrefixOpExpr(c, m, scope);
- try call_params.append(arg);
- switch (m.next().?) {
- .Comma => _ = try appendToken(c, .Comma, ","),
- .RParen => break,
- else => {
- try m.fail(c, "unable to translate C expr: expected ',' or ')'", .{});
- return error.ParseError;
- },
- }
- }
- const call_node = try ast.Node.Call.alloc(c.arena, call_params.items.len);
- call_node.* = .{
- .lhs = node,
- .params_len = call_params.items.len,
- .async_token = null,
- .rtoken = try appendToken(c, .RParen, ")"),
- };
- mem.copy(*ast.Node, call_node.params(), call_params.items);
- node = &call_node.base;
- continue;
- },
- .LBrace => {
- // must come immediately after `node`
- _ = try appendToken(c, .Comma, ",");
-
- const dot = try appendToken(c, .Period, ".");
- _ = try appendToken(c, .LBrace, "{");
-
- var init_vals = std.ArrayList(*ast.Node).init(c.gpa);
- defer init_vals.deinit();
-
- while (true) {
- const val = try parseCPrefixOpExpr(c, m, scope);
- try init_vals.append(val);
- switch (m.next().?) {
- .Comma => _ = try appendToken(c, .Comma, ","),
- .RBrace => break,
- else => {
- try m.fail(c, "unable to translate C expr: expected ',' or '}}'", .{});
- return error.ParseError;
- },
- }
- }
- const tuple_node = try ast.Node.StructInitializerDot.alloc(c.arena, init_vals.items.len);
- tuple_node.* = .{
- .dot = dot,
- .list_len = init_vals.items.len,
- .rtoken = try appendToken(c, .RBrace, "}"),
- };
- mem.copy(*ast.Node, tuple_node.list(), init_vals.items);
-
- //(@import("std").mem.zeroInit(T, .{x}))
- const import_fn_call = try c.createBuiltinCall("@import", 1);
- const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
- import_fn_call.params()[0] = std_node;
- import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
- const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "mem");
- const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "zeroInit");
-
- const zero_init_call = try c.createCall(outer_field_access, 2);
- zero_init_call.params()[0] = node;
- zero_init_call.params()[1] = &tuple_node.base;
- zero_init_call.rtoken = try appendToken(c, .RParen, ")");
-
- node = &zero_init_call.base;
- continue;
- },
- .BangEqual => {
- op_token = try appendToken(c, .BangEqual, "!=");
- op_id = .BangEqual;
- },
- .EqualEqual => {
- op_token = try appendToken(c, .EqualEqual, "==");
- op_id = .EqualEqual;
- },
- .Slash => {
- op_id = .Div;
- op_token = try appendToken(c, .Slash, "/");
- },
- .Percent => {
- op_id = .Mod;
- op_token = try appendToken(c, .Percent, "%");
- },
- .StringLiteral => {
- op_id = .ArrayCat;
- op_token = try appendToken(c, .PlusPlus, "++");
-
- m.i -= 1;
- },
- .Identifier => {
- op_id = .ArrayCat;
- op_token = try appendToken(c, .PlusPlus, "++");
-
- m.i -= 1;
- },
- else => {
- m.i -= 1;
- return node;
- },
- }
- const cast_fn = if (bool_op) macroIntToBool else macroBoolToInt;
- const lhs_node = try cast_fn(c, node);
- const rhs_node = try parseCPrefixOpExpr(c, m, scope);
- const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
- op_node.* = .{
- .base = .{ .tag = op_id },
- .op_token = op_token,
- .lhs = lhs_node,
- .rhs = try cast_fn(c, rhs_node),
- };
- node = &op_node.base;
- }
-}
-
-fn parseCPrefixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
- switch (m.next().?) {
- .Bang => {
- const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!");
- node.rhs = try parseCPrefixOpExpr(c, m, scope);
- return &node.base;
- },
- .Minus => {
- const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-");
- node.rhs = try parseCPrefixOpExpr(c, m, scope);
- return &node.base;
- },
- .Plus => return try parseCPrefixOpExpr(c, m, scope),
- .Tilde => {
- const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~");
- node.rhs = try parseCPrefixOpExpr(c, m, scope);
- return &node.base;
- },
- .Asterisk => {
- const node = try parseCPrefixOpExpr(c, m, scope);
- return try transCreateNodePtrDeref(c, node);
- },
- .Ampersand => {
- const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&");
- node.rhs = try parseCPrefixOpExpr(c, m, scope);
- return &node.base;
- },
- .Keyword_sizeof => {
- const inner = if (m.peek().? == .LParen) blk: {
- _ = m.next();
- const inner = try parseCExpr(c, m, scope);
- if (m.next().? != .RParen) {
- try m.fail(c, "unable to translate C expr: expected ')'", .{});
- return error.ParseError;
- }
- break :blk inner;
- } else try parseCPrefixOpExpr(c, m, scope);
-
- //(@import("std").meta.sizeof(dest, x))
- const import_fn_call = try c.createBuiltinCall("@import", 1);
- const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
- import_fn_call.params()[0] = std_node;
- import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
- const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "meta");
- const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "sizeof");
-
- const sizeof_call = try c.createCall(outer_field_access, 1);
- sizeof_call.params()[0] = inner;
- sizeof_call.rtoken = try appendToken(c, .RParen, ")");
- return &sizeof_call.base;
- },
- .Keyword_alignof => {
- // TODO this won't work if using 's
- // #define alignof _Alignof
- if (m.next().? != .LParen) {
- try m.fail(c, "unable to translate C expr: expected '('", .{});
- return error.ParseError;
- }
- const inner = try parseCExpr(c, m, scope);
- if (m.next().? != .RParen) {
- try m.fail(c, "unable to translate C expr: expected ')'", .{});
- return error.ParseError;
- }
-
- const builtin_call = try c.createBuiltinCall("@alignOf", 1);
- builtin_call.params()[0] = inner;
- builtin_call.rparen_token = try appendToken(c, .RParen, ")");
- return &builtin_call.base;
- },
- else => {
- m.i -= 1;
- return try parseCSuffixOpExpr(c, m, scope);
- },
- }
-}
-
-fn tokenSlice(c: *Context, token: ast.TokenIndex) []u8 {
- const tok = c.token_locs.items[token];
- const slice = c.source_buffer.span()[tok.start..tok.end];
- return if (mem.startsWith(u8, slice, "@\""))
- slice[2 .. slice.len - 1]
- else
- slice;
-}
-
-fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {
- switch (node.tag) {
- .ContainerDecl,
- .AddressOf,
- .Await,
- .BitNot,
- .BoolNot,
- .OptionalType,
- .Negation,
- .NegationWrap,
- .Resume,
- .Try,
- .ArrayType,
- .ArrayTypeSentinel,
- .PtrType,
- .SliceType,
- => return node,
-
- .Identifier => {
- const ident = node.castTag(.Identifier).?;
- if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| {
- if (value.cast(ast.Node.VarDecl)) |var_decl|
- return getContainer(c, var_decl.getInitNode().?);
- }
- },
-
- .Period => {
- const infix = node.castTag(.Period).?;
-
- if (getContainerTypeOf(c, infix.lhs)) |ty_node| {
- if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
- for (container.fieldsAndDecls()) |field_ref| {
- const field = field_ref.cast(ast.Node.ContainerField).?;
- const ident = infix.rhs.castTag(.Identifier).?;
- if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {
- return getContainer(c, field.type_expr.?);
- }
- }
- }
- }
- },
-
- else => {},
- }
- return null;
-}
-
-fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {
- if (ref.castTag(.Identifier)) |ident| {
- if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| {
- if (value.cast(ast.Node.VarDecl)) |var_decl| {
- if (var_decl.getTypeNode()) |ty|
- return getContainer(c, ty);
- }
- }
- } else if (ref.castTag(.Period)) |infix| {
- if (getContainerTypeOf(c, infix.lhs)) |ty_node| {
- if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
- for (container.fieldsAndDecls()) |field_ref| {
- const field = field_ref.cast(ast.Node.ContainerField).?;
- const ident = infix.rhs.castTag(.Identifier).?;
- if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {
- return getContainer(c, field.type_expr.?);
- }
- }
- } else
- return ty_node;
- }
- }
- return null;
-}
-
-fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto {
- const init = if (ref.cast(ast.Node.VarDecl)) |v| v.getInitNode().? else return null;
- if (getContainerTypeOf(c, init)) |ty_node| {
- if (ty_node.castTag(.OptionalType)) |prefix| {
- if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| {
- return fn_proto;
- }
- }
- }
- return null;
-}
-
-fn addMacros(c: *Context) !void {
- var it = c.global_scope.macro_table.iterator();
- while (it.next()) |kv| {
- if (getFnProto(c, kv.value)) |proto_node| {
- // If a macro aliases a global variable which is a function pointer, we conclude that
- // the macro is intended to represent a function that assumes the function pointer
- // variable is non-null and calls it.
- try addTopLevelDecl(c, kv.key, try transCreateNodeMacroFn(c, kv.key, kv.value, proto_node));
- } else {
- try addTopLevelDecl(c, kv.key, kv.value);
- }
- }
-}
diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig
deleted file mode 100644
index 49663955124152ab8dc90a20e29fd612edd5841f..0000000000000000000000000000000000000000
--- a/src-self-hosted/type.zig
+++ /dev/null
@@ -1,3075 +0,0 @@
-const std = @import("std");
-const Value = @import("value.zig").Value;
-const assert = std.debug.assert;
-const Allocator = std.mem.Allocator;
-const Target = std.Target;
-const Module = @import("Module.zig");
-
-/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
-/// It's important for this type to be small.
-/// Types are not de-duplicated, which helps with multi-threading since it obviates the requirement
-/// of obtaining a lock on a global type table, as well as making the
-/// garbage collection bookkeeping simpler.
-/// This union takes advantage of the fact that the first page of memory
-/// is unmapped, giving us 4096 possible enum tags that have no payload.
-pub const Type = extern union {
- /// If the tag value is less than Tag.no_payload_count, then no pointer
- /// dereference is needed.
- tag_if_small_enough: usize,
- ptr_otherwise: *Payload,
-
- pub fn zigTypeTag(self: Type) std.builtin.TypeId {
- switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .int_signed,
- .int_unsigned,
- => return .Int,
-
- .f16,
- .f32,
- .f64,
- .f128,
- => return .Float,
-
- .c_void => return .Opaque,
- .bool => return .Bool,
- .void => return .Void,
- .type => return .Type,
- .error_set, .error_set_single, .anyerror => return .ErrorSet,
- .comptime_int => return .ComptimeInt,
- .comptime_float => return .ComptimeFloat,
- .noreturn => return .NoReturn,
- .@"null" => return .Null,
- .@"undefined" => return .Undefined,
-
- .fn_noreturn_no_args => return .Fn,
- .fn_void_no_args => return .Fn,
- .fn_naked_noreturn_no_args => return .Fn,
- .fn_ccc_void_no_args => return .Fn,
- .function => return .Fn,
-
- .array, .array_u8_sentinel_0, .array_u8, .array_sentinel => return .Array,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .pointer,
- => return .Pointer,
-
- .optional,
- .optional_single_const_pointer,
- .optional_single_mut_pointer,
- => return .Optional,
- .enum_literal => return .EnumLiteral,
-
- .anyerror_void_error_union, .error_union => return .ErrorUnion,
-
- .anyframe_T, .@"anyframe" => return .AnyFrame,
- }
- }
-
- pub fn initTag(comptime small_tag: Tag) Type {
- comptime assert(@enumToInt(small_tag) < Tag.no_payload_count);
- return .{ .tag_if_small_enough = @enumToInt(small_tag) };
- }
-
- pub fn initPayload(payload: *Payload) Type {
- assert(@enumToInt(payload.tag) >= Tag.no_payload_count);
- return .{ .ptr_otherwise = payload };
- }
-
- pub fn tag(self: Type) Tag {
- if (self.tag_if_small_enough < Tag.no_payload_count) {
- return @intToEnum(Tag, @intCast(@TagType(Tag), self.tag_if_small_enough));
- } else {
- return self.ptr_otherwise.tag;
- }
- }
-
- pub fn cast(self: Type, comptime T: type) ?*T {
- if (self.tag_if_small_enough < Tag.no_payload_count)
- return null;
-
- const expected_tag = std.meta.fieldInfo(T, "base").default_value.?.tag;
- if (self.ptr_otherwise.tag != expected_tag)
- return null;
-
- return @fieldParentPtr(T, "base", self.ptr_otherwise);
- }
-
- pub fn castPointer(self: Type) ?*Payload.PointerSimple {
- return switch (self.tag()) {
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .optional_single_const_pointer,
- .optional_single_mut_pointer,
- => @fieldParentPtr(Payload.PointerSimple, "base", self.ptr_otherwise),
- else => null,
- };
- }
-
- pub fn eql(a: Type, b: Type) bool {
- // As a shortcut, if the small tags / addresses match, we're done.
- if (a.tag_if_small_enough == b.tag_if_small_enough)
- return true;
- const zig_tag_a = a.zigTypeTag();
- const zig_tag_b = b.zigTypeTag();
- if (zig_tag_a != zig_tag_b)
- return false;
- switch (zig_tag_a) {
- .EnumLiteral => return true,
- .Type => return true,
- .Void => return true,
- .Bool => return true,
- .NoReturn => return true,
- .ComptimeFloat => return true,
- .ComptimeInt => return true,
- .Undefined => return true,
- .Null => return true,
- .AnyFrame => {
- return a.elemType().eql(b.elemType());
- },
- .Pointer => {
- // Hot path for common case:
- if (a.castPointer()) |a_payload| {
- if (b.castPointer()) |b_payload| {
- return a.tag() == b.tag() and eql(a_payload.pointee_type, b_payload.pointee_type);
- }
- }
- const is_slice_a = isSlice(a);
- const is_slice_b = isSlice(b);
- if (is_slice_a != is_slice_b)
- return false;
- @panic("TODO implement more pointer Type equality comparison");
- },
- .Int => {
- // Detect that e.g. u64 != usize, even if the bits match on a particular target.
- const a_is_named_int = a.isNamedInt();
- const b_is_named_int = b.isNamedInt();
- if (a_is_named_int != b_is_named_int)
- return false;
- if (a_is_named_int)
- return a.tag() == b.tag();
- // Remaining cases are arbitrary sized integers.
- // The target will not be branched upon, because we handled target-dependent cases above.
- const info_a = a.intInfo(@as(Target, undefined));
- const info_b = b.intInfo(@as(Target, undefined));
- return info_a.signed == info_b.signed and info_a.bits == info_b.bits;
- },
- .Array => {
- if (a.arrayLen() != b.arrayLen())
- return false;
- if (!a.elemType().eql(b.elemType()))
- return false;
- const sentinel_a = a.sentinel();
- const sentinel_b = b.sentinel();
- if (sentinel_a) |sa| {
- if (sentinel_b) |sb| {
- return sa.eql(sb);
- } else {
- return false;
- }
- } else {
- return sentinel_b == null;
- }
- },
- .Fn => {
- if (!a.fnReturnType().eql(b.fnReturnType()))
- return false;
- if (a.fnCallingConvention() != b.fnCallingConvention())
- return false;
- const a_param_len = a.fnParamLen();
- const b_param_len = b.fnParamLen();
- if (a_param_len != b_param_len)
- return false;
- var i: usize = 0;
- while (i < a_param_len) : (i += 1) {
- if (!a.fnParamType(i).eql(b.fnParamType(i)))
- return false;
- }
- return true;
- },
- .Optional => {
- var buf_a: Payload.PointerSimple = undefined;
- var buf_b: Payload.PointerSimple = undefined;
- return a.optionalChild(&buf_a).eql(b.optionalChild(&buf_b));
- },
- .Float,
- .Struct,
- .ErrorUnion,
- .ErrorSet,
- .Enum,
- .Union,
- .BoundFn,
- .Opaque,
- .Frame,
- .Vector,
- => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),
- }
- }
-
- pub fn hash(self: Type) u64 {
- var hasher = std.hash.Wyhash.init(0);
- const zig_type_tag = self.zigTypeTag();
- std.hash.autoHash(&hasher, zig_type_tag);
- switch (zig_type_tag) {
- .Type,
- .Void,
- .Bool,
- .NoReturn,
- .ComptimeFloat,
- .ComptimeInt,
- .Undefined,
- .Null,
- => {}, // The zig type tag is all that is needed to distinguish.
-
- .Pointer => {
- // TODO implement more pointer type hashing
- },
- .Int => {
- // Detect that e.g. u64 != usize, even if the bits match on a particular target.
- if (self.isNamedInt()) {
- std.hash.autoHash(&hasher, self.tag());
- } else {
- // Remaining cases are arbitrary sized integers.
- // The target will not be branched upon, because we handled target-dependent cases above.
- const info = self.intInfo(@as(Target, undefined));
- std.hash.autoHash(&hasher, info.signed);
- std.hash.autoHash(&hasher, info.bits);
- }
- },
- .Array => {
- std.hash.autoHash(&hasher, self.arrayLen());
- std.hash.autoHash(&hasher, self.elemType().hash());
- // TODO hash array sentinel
- },
- .Fn => {
- std.hash.autoHash(&hasher, self.fnReturnType().hash());
- std.hash.autoHash(&hasher, self.fnCallingConvention());
- const params_len = self.fnParamLen();
- std.hash.autoHash(&hasher, params_len);
- var i: usize = 0;
- while (i < params_len) : (i += 1) {
- std.hash.autoHash(&hasher, self.fnParamType(i).hash());
- }
- },
- .Optional => {
- var buf: Payload.PointerSimple = undefined;
- std.hash.autoHash(&hasher, self.optionalChild(&buf).hash());
- },
- .Float,
- .Struct,
- .ErrorUnion,
- .ErrorSet,
- .Enum,
- .Union,
- .BoundFn,
- .Opaque,
- .Frame,
- .AnyFrame,
- .Vector,
- .EnumLiteral,
- => {
- // TODO implement more type hashing
- },
- }
- return hasher.final();
- }
-
- pub fn copy(self: Type, allocator: *Allocator) error{OutOfMemory}!Type {
- if (self.tag_if_small_enough < Tag.no_payload_count) {
- return Type{ .tag_if_small_enough = self.tag_if_small_enough };
- } else switch (self.ptr_otherwise.tag) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .c_void,
- .f16,
- .f32,
- .f64,
- .f128,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .enum_literal,
- .anyerror_void_error_union,
- .@"anyframe",
- => unreachable,
-
- .array_u8_sentinel_0 => return self.copyPayloadShallow(allocator, Payload.Array_u8_Sentinel0),
- .array_u8 => return self.copyPayloadShallow(allocator, Payload.Array_u8),
- .array => {
- const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(Payload.Array);
- new_payload.* = .{
- .base = payload.base,
- .len = payload.len,
- .elem_type = try payload.elem_type.copy(allocator),
- };
- return Type{ .ptr_otherwise = &new_payload.base };
- },
- .array_sentinel => {
- const payload = @fieldParentPtr(Payload.ArraySentinel, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(Payload.ArraySentinel);
- new_payload.* = .{
- .base = payload.base,
- .len = payload.len,
- .sentinel = try payload.sentinel.copy(allocator),
- .elem_type = try payload.elem_type.copy(allocator),
- };
- return Type{ .ptr_otherwise = &new_payload.base };
- },
- .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned),
- .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned),
- .function => {
- const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(Payload.Function);
- const param_types = try allocator.alloc(Type, payload.param_types.len);
- for (payload.param_types) |param_type, i| {
- param_types[i] = try param_type.copy(allocator);
- }
- new_payload.* = .{
- .base = payload.base,
- .return_type = try payload.return_type.copy(allocator),
- .param_types = param_types,
- .cc = payload.cc,
- };
- return Type{ .ptr_otherwise = &new_payload.base };
- },
- .optional => return self.copyPayloadSingleField(allocator, Payload.Optional, "child_type"),
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- => return self.copyPayloadSingleField(allocator, Payload.PointerSimple, "pointee_type"),
- .anyframe_T => return self.copyPayloadSingleField(allocator, Payload.AnyFrame, "return_type"),
-
- .pointer => {
- const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(Payload.Pointer);
- new_payload.* = .{
- .base = payload.base,
-
- .pointee_type = try payload.pointee_type.copy(allocator),
- .sentinel = if (payload.sentinel) |some| try some.copy(allocator) else null,
- .@"align" = payload.@"align",
- .bit_offset = payload.bit_offset,
- .host_size = payload.host_size,
- .@"allowzero" = payload.@"allowzero",
- .mutable = payload.mutable,
- .@"volatile" = payload.@"volatile",
- .size = payload.size,
- };
- return Type{ .ptr_otherwise = &new_payload.base };
- },
- .error_union => {
- const payload = @fieldParentPtr(Payload.ErrorUnion, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(Payload.ErrorUnion);
- new_payload.* = .{
- .base = payload.base,
-
- .error_set = try payload.error_set.copy(allocator),
- .payload = try payload.payload.copy(allocator),
- };
- return Type{ .ptr_otherwise = &new_payload.base };
- },
- .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
- .error_set_single => return self.copyPayloadShallow(allocator, Payload.ErrorSetSingle),
- }
- }
-
- fn copyPayloadShallow(self: Type, allocator: *Allocator, comptime T: type) error{OutOfMemory}!Type {
- const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(T);
- new_payload.* = payload.*;
- return Type{ .ptr_otherwise = &new_payload.base };
- }
-
- fn copyPayloadSingleField(self: Type, allocator: *Allocator, comptime T: type, comptime field_name: []const u8) error{OutOfMemory}!Type {
- const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(T);
- new_payload.base = payload.base;
- @field(new_payload, field_name) = try @field(payload, field_name).copy(allocator);
- return Type{ .ptr_otherwise = &new_payload.base };
- }
-
- pub fn format(
- self: Type,
- comptime fmt: []const u8,
- options: std.fmt.FormatOptions,
- out_stream: anytype,
- ) @TypeOf(out_stream).Error!void {
- comptime assert(fmt.len == 0);
- var ty = self;
- while (true) {
- const t = ty.tag();
- switch (t) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .c_void,
- .f16,
- .f32,
- .f64,
- .f128,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- => return out_stream.writeAll(@tagName(t)),
-
- .enum_literal => return out_stream.writeAll("@Type(.EnumLiteral)"),
- .@"null" => return out_stream.writeAll("@Type(.Null)"),
- .@"undefined" => return out_stream.writeAll("@Type(.Undefined)"),
-
- .@"anyframe" => return out_stream.writeAll("anyframe"),
- .anyerror_void_error_union => return out_stream.writeAll("anyerror!void"),
- .const_slice_u8 => return out_stream.writeAll("[]const u8"),
- .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),
- .fn_void_no_args => return out_stream.writeAll("fn() void"),
- .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
- .fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"),
- .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"),
- .function => {
- const payload = @fieldParentPtr(Payload.Function, "base", ty.ptr_otherwise);
- try out_stream.writeAll("fn(");
- for (payload.param_types) |param_type, i| {
- if (i != 0) try out_stream.writeAll(", ");
- try param_type.format("", .{}, out_stream);
- }
- try out_stream.writeAll(") ");
- ty = payload.return_type;
- continue;
- },
-
- .anyframe_T => {
- const payload = @fieldParentPtr(Payload.AnyFrame, "base", ty.ptr_otherwise);
- try out_stream.print("anyframe->", .{});
- ty = payload.return_type;
- continue;
- },
- .array_u8 => {
- const payload = @fieldParentPtr(Payload.Array_u8, "base", ty.ptr_otherwise);
- return out_stream.print("[{}]u8", .{payload.len});
- },
- .array_u8_sentinel_0 => {
- const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", ty.ptr_otherwise);
- return out_stream.print("[{}:0]u8", .{payload.len});
- },
- .array => {
- const payload = @fieldParentPtr(Payload.Array, "base", ty.ptr_otherwise);
- try out_stream.print("[{}]", .{payload.len});
- ty = payload.elem_type;
- continue;
- },
- .array_sentinel => {
- const payload = @fieldParentPtr(Payload.ArraySentinel, "base", ty.ptr_otherwise);
- try out_stream.print("[{}:{}]", .{ payload.len, payload.sentinel });
- ty = payload.elem_type;
- continue;
- },
- .single_const_pointer => {
- const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
- try out_stream.writeAll("*const ");
- ty = payload.pointee_type;
- continue;
- },
- .single_mut_pointer => {
- const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
- try out_stream.writeAll("*");
- ty = payload.pointee_type;
- continue;
- },
- .many_const_pointer => {
- const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
- try out_stream.writeAll("[*]const ");
- ty = payload.pointee_type;
- continue;
- },
- .many_mut_pointer => {
- const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
- try out_stream.writeAll("[*]");
- ty = payload.pointee_type;
- continue;
- },
- .c_const_pointer => {
- const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
- try out_stream.writeAll("[*c]const ");
- ty = payload.pointee_type;
- continue;
- },
- .c_mut_pointer => {
- const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
- try out_stream.writeAll("[*c]");
- ty = payload.pointee_type;
- continue;
- },
- .const_slice => {
- const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
- try out_stream.writeAll("[]const ");
- ty = payload.pointee_type;
- continue;
- },
- .mut_slice => {
- const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
- try out_stream.writeAll("[]");
- ty = payload.pointee_type;
- continue;
- },
- .int_signed => {
- const payload = @fieldParentPtr(Payload.IntSigned, "base", ty.ptr_otherwise);
- return out_stream.print("i{}", .{payload.bits});
- },
- .int_unsigned => {
- const payload = @fieldParentPtr(Payload.IntUnsigned, "base", ty.ptr_otherwise);
- return out_stream.print("u{}", .{payload.bits});
- },
- .optional => {
- const payload = @fieldParentPtr(Payload.Optional, "base", ty.ptr_otherwise);
- try out_stream.writeByte('?');
- ty = payload.child_type;
- continue;
- },
- .optional_single_const_pointer => {
- const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
- try out_stream.writeAll("?*const ");
- ty = payload.pointee_type;
- continue;
- },
- .optional_single_mut_pointer => {
- const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
- try out_stream.writeAll("?*");
- ty = payload.pointee_type;
- continue;
- },
-
- .pointer => {
- const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
- if (payload.sentinel) |some| switch (payload.size) {
- .One, .C => unreachable,
- .Many => try out_stream.print("[*:{}]", .{some}),
- .Slice => try out_stream.print("[:{}]", .{some}),
- } else switch (payload.size) {
- .One => try out_stream.writeAll("*"),
- .Many => try out_stream.writeAll("[*]"),
- .C => try out_stream.writeAll("[*c]"),
- .Slice => try out_stream.writeAll("[]"),
- }
- if (payload.@"align" != 0) {
- try out_stream.print("align({}", .{payload.@"align"});
-
- if (payload.bit_offset != 0) {
- try out_stream.print(":{}:{}", .{ payload.bit_offset, payload.host_size });
- }
- try out_stream.writeAll(") ");
- }
- if (!payload.mutable) try out_stream.writeAll("const ");
- if (payload.@"volatile") try out_stream.writeAll("volatile ");
- if (payload.@"allowzero") try out_stream.writeAll("allowzero ");
-
- ty = payload.pointee_type;
- continue;
- },
- .error_union => {
- const payload = @fieldParentPtr(Payload.ErrorUnion, "base", ty.ptr_otherwise);
- try payload.error_set.format("", .{}, out_stream);
- try out_stream.writeAll("!");
- ty = payload.payload;
- continue;
- },
- .error_set => {
- const payload = @fieldParentPtr(Payload.ErrorSet, "base", ty.ptr_otherwise);
- return out_stream.writeAll(std.mem.spanZ(payload.decl.name));
- },
- .error_set_single => {
- const payload = @fieldParentPtr(Payload.ErrorSetSingle, "base", ty.ptr_otherwise);
- return out_stream.print("error{{{}}}", .{payload.name});
- },
- }
- unreachable;
- }
- }
-
- pub fn toValue(self: Type, allocator: *Allocator) Allocator.Error!Value {
- switch (self.tag()) {
- .u8 => return Value.initTag(.u8_type),
- .i8 => return Value.initTag(.i8_type),
- .u16 => return Value.initTag(.u16_type),
- .i16 => return Value.initTag(.i16_type),
- .u32 => return Value.initTag(.u32_type),
- .i32 => return Value.initTag(.i32_type),
- .u64 => return Value.initTag(.u64_type),
- .i64 => return Value.initTag(.i64_type),
- .usize => return Value.initTag(.usize_type),
- .isize => return Value.initTag(.isize_type),
- .c_short => return Value.initTag(.c_short_type),
- .c_ushort => return Value.initTag(.c_ushort_type),
- .c_int => return Value.initTag(.c_int_type),
- .c_uint => return Value.initTag(.c_uint_type),
- .c_long => return Value.initTag(.c_long_type),
- .c_ulong => return Value.initTag(.c_ulong_type),
- .c_longlong => return Value.initTag(.c_longlong_type),
- .c_ulonglong => return Value.initTag(.c_ulonglong_type),
- .c_longdouble => return Value.initTag(.c_longdouble_type),
- .c_void => return Value.initTag(.c_void_type),
- .f16 => return Value.initTag(.f16_type),
- .f32 => return Value.initTag(.f32_type),
- .f64 => return Value.initTag(.f64_type),
- .f128 => return Value.initTag(.f128_type),
- .bool => return Value.initTag(.bool_type),
- .void => return Value.initTag(.void_type),
- .type => return Value.initTag(.type_type),
- .anyerror => return Value.initTag(.anyerror_type),
- .comptime_int => return Value.initTag(.comptime_int_type),
- .comptime_float => return Value.initTag(.comptime_float_type),
- .noreturn => return Value.initTag(.noreturn_type),
- .@"null" => return Value.initTag(.null_type),
- .@"undefined" => return Value.initTag(.undefined_type),
- .fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type),
- .fn_void_no_args => return Value.initTag(.fn_void_no_args_type),
- .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),
- .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type),
- .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),
- .const_slice_u8 => return Value.initTag(.const_slice_u8_type),
- .enum_literal => return Value.initTag(.enum_literal_type),
- else => {
- const ty_payload = try allocator.create(Value.Payload.Ty);
- ty_payload.* = .{ .ty = self };
- return Value.initPayload(&ty_payload.base);
- },
- }
- }
-
- pub fn hasCodeGenBits(self: Type) bool {
- return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .bool,
- .anyerror,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .array_u8_sentinel_0,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => true,
- // TODO lazy types
- .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0,
- .array_u8 => self.arrayLen() != 0,
- .array_sentinel, .single_const_pointer, .single_mut_pointer, .many_const_pointer, .many_mut_pointer, .c_const_pointer, .c_mut_pointer, .const_slice, .mut_slice, .pointer => self.elemType().hasCodeGenBits(),
- .int_signed => self.cast(Payload.IntSigned).?.bits != 0,
- .int_unsigned => self.cast(Payload.IntUnsigned).?.bits != 0,
-
- .error_union => {
- const payload = self.cast(Payload.ErrorUnion).?;
- return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits();
- },
-
- .c_void,
- .void,
- .type,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .enum_literal,
- => false,
- };
- }
-
- pub fn isNoReturn(self: Type) bool {
- return self.zigTypeTag() == .NoReturn;
- }
-
- /// Asserts that hasCodeGenBits() is true.
- pub fn abiAlignment(self: Type, target: Target) u32 {
- return switch (self.tag()) {
- .u8,
- .i8,
- .bool,
- .array_u8_sentinel_0,
- .array_u8,
- => return 1,
-
- .fn_noreturn_no_args, // represents machine code; not a pointer
- .fn_void_no_args, // represents machine code; not a pointer
- .fn_naked_noreturn_no_args, // represents machine code; not a pointer
- .fn_ccc_void_no_args, // represents machine code; not a pointer
- .function, // represents machine code; not a pointer
- => return switch (target.cpu.arch) {
- .arm => 4,
- .riscv64 => 2,
- else => 1,
- },
-
- .i16, .u16 => return 2,
- .i32, .u32 => return 4,
- .i64, .u64 => return 8,
-
- .isize,
- .usize,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .optional_single_const_pointer,
- .optional_single_mut_pointer,
- .@"anyframe",
- .anyframe_T,
- => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
-
- .pointer => {
- const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
-
- if (payload.@"align" != 0) return payload.@"align";
- return @divExact(target.cpu.arch.ptrBitWidth(), 8);
- },
-
- .c_short => return @divExact(CType.short.sizeInBits(target), 8),
- .c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8),
- .c_int => return @divExact(CType.int.sizeInBits(target), 8),
- .c_uint => return @divExact(CType.uint.sizeInBits(target), 8),
- .c_long => return @divExact(CType.long.sizeInBits(target), 8),
- .c_ulong => return @divExact(CType.ulong.sizeInBits(target), 8),
- .c_longlong => return @divExact(CType.longlong.sizeInBits(target), 8),
- .c_ulonglong => return @divExact(CType.ulonglong.sizeInBits(target), 8),
-
- .f16 => return 2,
- .f32 => return 4,
- .f64 => return 8,
- .f128 => return 16,
- .c_longdouble => return 16,
-
- .error_set,
- .error_set_single,
- .anyerror_void_error_union,
- .anyerror,
- => return 2, // TODO revisit this when we have the concept of the error tag type
-
- .array, .array_sentinel => return self.elemType().abiAlignment(target),
-
- .int_signed, .int_unsigned => {
- const bits: u16 = if (self.cast(Payload.IntSigned)) |pl|
- pl.bits
- else if (self.cast(Payload.IntUnsigned)) |pl|
- pl.bits
- else
- unreachable;
-
- return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
- },
-
- .optional => {
- var buf: Payload.PointerSimple = undefined;
- const child_type = self.optionalChild(&buf);
- if (!child_type.hasCodeGenBits()) return 1;
-
- if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr())
- return @divExact(target.cpu.arch.ptrBitWidth(), 8);
-
- return child_type.abiAlignment(target);
- },
-
- .error_union => {
- const payload = self.cast(Payload.ErrorUnion).?;
- if (!payload.error_set.hasCodeGenBits()) {
- return payload.payload.abiAlignment(target);
- } else if (!payload.payload.hasCodeGenBits()) {
- return payload.error_set.abiAlignment(target);
- }
- @panic("TODO abiAlignment error union");
- },
-
- .c_void,
- .void,
- .type,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .enum_literal,
- => unreachable,
- };
- }
-
- /// Asserts the type has the ABI size already resolved.
- pub fn abiSize(self: Type, target: Target) u64 {
- return switch (self.tag()) {
- .fn_noreturn_no_args => unreachable, // represents machine code; not a pointer
- .fn_void_no_args => unreachable, // represents machine code; not a pointer
- .fn_naked_noreturn_no_args => unreachable, // represents machine code; not a pointer
- .fn_ccc_void_no_args => unreachable, // represents machine code; not a pointer
- .function => unreachable, // represents machine code; not a pointer
- .c_void => unreachable,
- .void => unreachable,
- .type => unreachable,
- .comptime_int => unreachable,
- .comptime_float => unreachable,
- .noreturn => unreachable,
- .@"null" => unreachable,
- .@"undefined" => unreachable,
- .enum_literal => unreachable,
- .single_const_pointer_to_comptime_int => unreachable,
-
- .u8,
- .i8,
- .bool,
- => return 1,
-
- .array_u8 => @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", self.ptr_otherwise).len,
- .array_u8_sentinel_0 => @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", self.ptr_otherwise).len + 1,
- .array => {
- const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise);
- const elem_size = std.math.max(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target));
- return payload.len * elem_size;
- },
- .array_sentinel => {
- const payload = @fieldParentPtr(Payload.ArraySentinel, "base", self.ptr_otherwise);
- const elem_size = std.math.max(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target));
- return (payload.len + 1) * elem_size;
- },
- .i16, .u16 => return 2,
- .i32, .u32 => return 4,
- .i64, .u64 => return 8,
-
- .@"anyframe", .anyframe_T, .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
-
- .const_slice,
- .mut_slice,
- => {
- if (self.elemType().hasCodeGenBits()) return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2;
- return @divExact(target.cpu.arch.ptrBitWidth(), 8);
- },
- .const_slice_u8 => return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2,
-
- .optional_single_const_pointer,
- .optional_single_mut_pointer,
- => {
- if (self.elemType().hasCodeGenBits()) return 1;
- return @divExact(target.cpu.arch.ptrBitWidth(), 8);
- },
-
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .pointer,
- => {
- if (self.elemType().hasCodeGenBits()) return 0;
- return @divExact(target.cpu.arch.ptrBitWidth(), 8);
- },
-
- .c_short => return @divExact(CType.short.sizeInBits(target), 8),
- .c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8),
- .c_int => return @divExact(CType.int.sizeInBits(target), 8),
- .c_uint => return @divExact(CType.uint.sizeInBits(target), 8),
- .c_long => return @divExact(CType.long.sizeInBits(target), 8),
- .c_ulong => return @divExact(CType.ulong.sizeInBits(target), 8),
- .c_longlong => return @divExact(CType.longlong.sizeInBits(target), 8),
- .c_ulonglong => return @divExact(CType.ulonglong.sizeInBits(target), 8),
-
- .f16 => return 2,
- .f32 => return 4,
- .f64 => return 8,
- .f128 => return 16,
- .c_longdouble => return 16,
-
- .error_set,
- .error_set_single,
- .anyerror_void_error_union,
- .anyerror,
- => return 2, // TODO revisit this when we have the concept of the error tag type
-
- .int_signed, .int_unsigned => {
- const bits: u16 = if (self.cast(Payload.IntSigned)) |pl|
- pl.bits
- else if (self.cast(Payload.IntUnsigned)) |pl|
- pl.bits
- else
- unreachable;
-
- return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
- },
-
- .optional => {
- var buf: Payload.PointerSimple = undefined;
- const child_type = self.optionalChild(&buf);
- if (!child_type.hasCodeGenBits()) return 1;
-
- if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr())
- return @divExact(target.cpu.arch.ptrBitWidth(), 8);
-
- // Optional types are represented as a struct with the child type as the first
- // field and a boolean as the second. Since the child type's abi alignment is
- // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal
- // to the child type's ABI alignment.
- return child_type.abiAlignment(target) + child_type.abiSize(target);
- },
-
- .error_union => {
- const payload = self.cast(Payload.ErrorUnion).?;
- if (!payload.error_set.hasCodeGenBits() and !payload.payload.hasCodeGenBits()) {
- return 0;
- } else if (!payload.error_set.hasCodeGenBits()) {
- return payload.payload.abiSize(target);
- } else if (!payload.payload.hasCodeGenBits()) {
- return payload.error_set.abiSize(target);
- }
- @panic("TODO abiSize error union");
- },
- };
- }
-
- pub fn isSinglePointer(self: Type) bool {
- return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .const_slice_u8,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => false,
-
- .single_const_pointer,
- .single_mut_pointer,
- .single_const_pointer_to_comptime_int,
- => true,
-
- .pointer => self.cast(Payload.Pointer).?.size == .One,
- };
- }
-
- pub fn isSlice(self: Type) bool {
- return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .single_const_pointer_to_comptime_int,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => false,
-
- .const_slice,
- .mut_slice,
- .const_slice_u8,
- => true,
-
- .pointer => self.cast(Payload.Pointer).?.size == .Slice,
- };
- }
-
- pub fn isConstPtr(self: Type) bool {
- return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .int_unsigned,
- .int_signed,
- .single_mut_pointer,
- .many_mut_pointer,
- .c_mut_pointer,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .mut_slice,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => false,
-
- .single_const_pointer,
- .many_const_pointer,
- .c_const_pointer,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .const_slice,
- => true,
-
- .pointer => !self.cast(Payload.Pointer).?.mutable,
- };
- }
-
- pub fn isVolatilePtr(self: Type) bool {
- return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .int_unsigned,
- .int_signed,
- .single_mut_pointer,
- .single_const_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => false,
-
- .pointer => {
- const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
- return payload.@"volatile";
- },
- };
- }
-
- pub fn isAllowzeroPtr(self: Type) bool {
- return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .int_unsigned,
- .int_signed,
- .single_mut_pointer,
- .single_const_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => false,
-
- .pointer => {
- const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
- return payload.@"allowzero";
- },
- };
- }
-
- /// Asserts that the type is an optional
- pub fn isPtrLikeOptional(self: Type) bool {
- switch (self.tag()) {
- .optional_single_const_pointer, .optional_single_mut_pointer => return true,
- .optional => {
- var buf: Payload.PointerSimple = undefined;
- const child_type = self.optionalChild(&buf);
- // optionals of zero sized pointers behave like bools
- if (!child_type.hasCodeGenBits()) return false;
-
- return child_type.zigTypeTag() == .Pointer and !child_type.isCPtr();
- },
- else => unreachable,
- }
- }
-
- /// Returns if type can be used for a runtime variable
- pub fn isValidVarType(self: Type, is_extern: bool) bool {
- var ty = self;
- while (true) switch (ty.zigTypeTag()) {
- .Bool,
- .Int,
- .Float,
- .ErrorSet,
- .Enum,
- .Frame,
- .AnyFrame,
- .Vector,
- => return true,
-
- .Opaque => return is_extern,
- .BoundFn,
- .ComptimeFloat,
- .ComptimeInt,
- .EnumLiteral,
- .NoReturn,
- .Type,
- .Void,
- .Undefined,
- .Null,
- => return false,
-
- .Optional => {
- var buf: Payload.PointerSimple = undefined;
- return ty.optionalChild(&buf).isValidVarType(is_extern);
- },
- .Pointer, .Array => ty = ty.elemType(),
-
- .ErrorUnion => @panic("TODO fn isValidVarType"),
- .Fn => @panic("TODO fn isValidVarType"),
- .Struct => @panic("TODO struct isValidVarType"),
- .Union => @panic("TODO union isValidVarType"),
- };
- }
-
- /// Asserts the type is a pointer or array type.
- pub fn elemType(self: Type) Type {
- return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_const_pointer,
- .optional_single_mut_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => unreachable,
-
- .array => self.cast(Payload.Array).?.elem_type,
- .array_sentinel => self.cast(Payload.ArraySentinel).?.elem_type,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- => self.castPointer().?.pointee_type,
- .array_u8, .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8),
- .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
- .pointer => self.cast(Payload.Pointer).?.pointee_type,
- };
- }
-
- /// Asserts that the type is an optional.
- pub fn optionalChild(self: Type, buf: *Payload.PointerSimple) Type {
- return switch (self.tag()) {
- .optional => self.cast(Payload.Optional).?.child_type,
- .optional_single_mut_pointer => {
- buf.* = .{
- .base = .{ .tag = .single_mut_pointer },
- .pointee_type = self.castPointer().?.pointee_type,
- };
- return Type.initPayload(&buf.base);
- },
- .optional_single_const_pointer => {
- buf.* = .{
- .base = .{ .tag = .single_const_pointer },
- .pointee_type = self.castPointer().?.pointee_type,
- };
- return Type.initPayload(&buf.base);
- },
- else => unreachable,
- };
- }
-
- /// Asserts that the type is an optional.
- /// Same as `optionalChild` but allocates the buffer if needed.
- pub fn optionalChildAlloc(self: Type, allocator: *Allocator) !Type {
- return switch (self.tag()) {
- .optional => self.cast(Payload.Optional).?.child_type,
- .optional_single_mut_pointer, .optional_single_const_pointer => {
- const payload = try allocator.create(Payload.PointerSimple);
- payload.* = .{
- .base = .{
- .tag = if (self.tag() == .optional_single_const_pointer)
- .single_const_pointer
- else
- .single_mut_pointer,
- },
- .pointee_type = self.castPointer().?.pointee_type,
- };
- return Type.initPayload(&payload.base);
- },
- else => unreachable,
- };
- }
-
- /// Asserts the type is an array or vector.
- pub fn arrayLen(self: Type) u64 {
- return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => unreachable,
-
- .array => self.cast(Payload.Array).?.len,
- .array_sentinel => self.cast(Payload.ArraySentinel).?.len,
- .array_u8 => self.cast(Payload.Array_u8).?.len,
- .array_u8_sentinel_0 => self.cast(Payload.Array_u8_Sentinel0).?.len,
- };
- }
-
- /// Asserts the type is an array, pointer or vector.
- pub fn sentinel(self: Type) ?Value {
- return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .const_slice,
- .mut_slice,
- .const_slice_u8,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => unreachable,
-
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .single_const_pointer_to_comptime_int,
- .array,
- .array_u8,
- => return null,
-
- .pointer => return self.cast(Payload.Pointer).?.sentinel,
- .array_sentinel => return self.cast(Payload.ArraySentinel).?.sentinel,
- .array_u8_sentinel_0 => return Value.initTag(.zero),
- };
- }
-
- /// Returns true if and only if the type is a fixed-width integer.
- pub fn isInt(self: Type) bool {
- return self.isSignedInt() or self.isUnsignedInt();
- }
-
- /// Returns true if and only if the type is a fixed-width, signed integer.
- pub fn isSignedInt(self: Type) bool {
- return switch (self.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .int_unsigned,
- .u8,
- .usize,
- .c_ushort,
- .c_uint,
- .c_ulong,
- .c_ulonglong,
- .u16,
- .u32,
- .u64,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => false,
-
- .int_signed,
- .i8,
- .isize,
- .c_short,
- .c_int,
- .c_long,
- .c_longlong,
- .i16,
- .i32,
- .i64,
- => true,
- };
- }
-
- /// Returns true if and only if the type is a fixed-width, unsigned integer.
- pub fn isUnsignedInt(self: Type) bool {
- return switch (self.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .int_signed,
- .i8,
- .isize,
- .c_short,
- .c_int,
- .c_long,
- .c_longlong,
- .i16,
- .i32,
- .i64,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => false,
-
- .int_unsigned,
- .u8,
- .usize,
- .c_ushort,
- .c_uint,
- .c_ulong,
- .c_ulonglong,
- .u16,
- .u32,
- .u64,
- => true,
- };
- }
-
- /// Asserts the type is an integer.
- pub fn intInfo(self: Type, target: Target) struct { signed: bool, bits: u16 } {
- return switch (self.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => unreachable,
-
- .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits },
- .int_signed => .{ .signed = true, .bits = self.cast(Payload.IntSigned).?.bits },
- .u8 => .{ .signed = false, .bits = 8 },
- .i8 => .{ .signed = true, .bits = 8 },
- .u16 => .{ .signed = false, .bits = 16 },
- .i16 => .{ .signed = true, .bits = 16 },
- .u32 => .{ .signed = false, .bits = 32 },
- .i32 => .{ .signed = true, .bits = 32 },
- .u64 => .{ .signed = false, .bits = 64 },
- .i64 => .{ .signed = true, .bits = 64 },
- .usize => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() },
- .isize => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() },
- .c_short => .{ .signed = true, .bits = CType.short.sizeInBits(target) },
- .c_ushort => .{ .signed = false, .bits = CType.ushort.sizeInBits(target) },
- .c_int => .{ .signed = true, .bits = CType.int.sizeInBits(target) },
- .c_uint => .{ .signed = false, .bits = CType.uint.sizeInBits(target) },
- .c_long => .{ .signed = true, .bits = CType.long.sizeInBits(target) },
- .c_ulong => .{ .signed = false, .bits = CType.ulong.sizeInBits(target) },
- .c_longlong => .{ .signed = true, .bits = CType.longlong.sizeInBits(target) },
- .c_ulonglong => .{ .signed = false, .bits = CType.ulonglong.sizeInBits(target) },
- };
- }
-
- pub fn isNamedInt(self: Type) bool {
- return switch (self.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .int_unsigned,
- .int_signed,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => false,
-
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- => true,
- };
- }
-
- pub fn isFloat(self: Type) bool {
- return switch (self.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- => true,
-
- else => false,
- };
- }
-
- /// Asserts the type is a fixed-size float.
- pub fn floatBits(self: Type, target: Target) u16 {
- return switch (self.tag()) {
- .f16 => 16,
- .f32 => 32,
- .f64 => 64,
- .f128 => 128,
- .c_longdouble => CType.longdouble.sizeInBits(target),
-
- else => unreachable,
- };
- }
-
- /// Asserts the type is a function.
- pub fn fnParamLen(self: Type) usize {
- return switch (self.tag()) {
- .fn_noreturn_no_args => 0,
- .fn_void_no_args => 0,
- .fn_naked_noreturn_no_args => 0,
- .fn_ccc_void_no_args => 0,
- .function => @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise).param_types.len,
-
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => unreachable,
- };
- }
-
- /// Asserts the type is a function. The length of the slice must be at least the length
- /// given by `fnParamLen`.
- pub fn fnParamTypes(self: Type, types: []Type) void {
- switch (self.tag()) {
- .fn_noreturn_no_args => return,
- .fn_void_no_args => return,
- .fn_naked_noreturn_no_args => return,
- .fn_ccc_void_no_args => return,
- .function => {
- const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
- std.mem.copy(Type, types, payload.param_types);
- },
-
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => unreachable,
- }
- }
-
- /// Asserts the type is a function.
- pub fn fnParamType(self: Type, index: usize) Type {
- switch (self.tag()) {
- .function => {
- const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
- return payload.param_types[index];
- },
-
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => unreachable,
- }
- }
-
- /// Asserts the type is a function.
- pub fn fnReturnType(self: Type) Type {
- return switch (self.tag()) {
- .fn_noreturn_no_args => Type.initTag(.noreturn),
- .fn_naked_noreturn_no_args => Type.initTag(.noreturn),
-
- .fn_void_no_args,
- .fn_ccc_void_no_args,
- => Type.initTag(.void),
-
- .function => @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise).return_type,
-
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => unreachable,
- };
- }
-
- /// Asserts the type is a function.
- pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention {
- return switch (self.tag()) {
- .fn_noreturn_no_args => .Unspecified,
- .fn_void_no_args => .Unspecified,
- .fn_naked_noreturn_no_args => .Naked,
- .fn_ccc_void_no_args => .C,
- .function => @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise).cc,
-
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => unreachable,
- };
- }
-
- /// Asserts the type is a function.
- pub fn fnIsVarArgs(self: Type) bool {
- return switch (self.tag()) {
- .fn_noreturn_no_args => false,
- .fn_void_no_args => false,
- .fn_naked_noreturn_no_args => false,
- .fn_ccc_void_no_args => false,
- .function => false,
-
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => unreachable,
- };
- }
-
- pub fn isNumeric(self: Type) bool {
- return switch (self.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .comptime_int,
- .comptime_float,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .int_unsigned,
- .int_signed,
- => true,
-
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => false,
- };
- }
-
- pub fn onePossibleValue(self: Type) ?Value {
- var ty = self;
- while (true) switch (ty.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .comptime_int,
- .comptime_float,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .bool,
- .type,
- .anyerror,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .single_const_pointer_to_comptime_int,
- .array_sentinel,
- .array_u8_sentinel_0,
- .const_slice_u8,
- .const_slice,
- .mut_slice,
- .c_void,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .anyerror_void_error_union,
- .anyframe_T,
- .@"anyframe",
- .error_union,
- .error_set,
- .error_set_single,
- => return null,
-
- .void => return Value.initTag(.void_value),
- .noreturn => return Value.initTag(.unreachable_value),
- .@"null" => return Value.initTag(.null_value),
- .@"undefined" => return Value.initTag(.undef),
-
- .int_unsigned => {
- if (ty.cast(Payload.IntUnsigned).?.bits == 0) {
- return Value.initTag(.zero);
- } else {
- return null;
- }
- },
- .int_signed => {
- if (ty.cast(Payload.IntSigned).?.bits == 0) {
- return Value.initTag(.zero);
- } else {
- return null;
- }
- },
- .array, .array_u8 => {
- if (ty.arrayLen() == 0)
- return Value.initTag(.empty_array);
- ty = ty.elemType();
- continue;
- },
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .single_const_pointer,
- .single_mut_pointer,
- => {
- const ptr = ty.castPointer().?;
- ty = ptr.pointee_type;
- continue;
- },
- .pointer => {
- ty = ty.cast(Payload.Pointer).?.pointee_type;
- continue;
- },
- };
- }
-
- pub fn isCPtr(self: Type) bool {
- return switch (self.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .comptime_int,
- .comptime_float,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .bool,
- .type,
- .anyerror,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .c_void,
- .void,
- .noreturn,
- .@"null",
- .@"undefined",
- .int_unsigned,
- .int_signed,
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .const_slice,
- .mut_slice,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => return false,
-
- .c_const_pointer,
- .c_mut_pointer,
- => return true,
-
- .pointer => self.cast(Payload.Pointer).?.size == .C,
- };
- }
-
- pub fn isIndexable(self: Type) bool {
- const zig_tag = self.zigTypeTag();
- // TODO tuples are indexable
- return zig_tag == .Array or zig_tag == .Vector or self.isSlice() or
- (self.isSinglePointer() and self.elemType().zigTypeTag() == .Array);
- }
-
- /// This enum does not directly correspond to `std.builtin.TypeId` because
- /// it has extra enum tags in it, as a way of using less memory. For example,
- /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
- /// but with different alignment values, in this data structure they are represented
- /// with different enum tags, because the the former requires more payload data than the latter.
- /// See `zigTypeTag` for the function that corresponds to `std.builtin.TypeId`.
- pub const Tag = enum {
- // The first section of this enum are tags that require no payload.
- u8,
- i8,
- u16,
- i16,
- u32,
- i32,
- u64,
- i64,
- usize,
- isize,
- c_short,
- c_ushort,
- c_int,
- c_uint,
- c_long,
- c_ulong,
- c_longlong,
- c_ulonglong,
- c_longdouble,
- f16,
- f32,
- f64,
- f128,
- c_void,
- bool,
- void,
- type,
- anyerror,
- comptime_int,
- comptime_float,
- noreturn,
- enum_literal,
- @"null",
- @"undefined",
- fn_noreturn_no_args,
- fn_void_no_args,
- fn_naked_noreturn_no_args,
- fn_ccc_void_no_args,
- single_const_pointer_to_comptime_int,
- anyerror_void_error_union,
- @"anyframe",
- const_slice_u8, // See last_no_payload_tag below.
- // After this, the tag requires a payload.
-
- array_u8,
- array_u8_sentinel_0,
- array,
- array_sentinel,
- pointer,
- single_const_pointer,
- single_mut_pointer,
- many_const_pointer,
- many_mut_pointer,
- c_const_pointer,
- c_mut_pointer,
- const_slice,
- mut_slice,
- int_signed,
- int_unsigned,
- function,
- optional,
- optional_single_mut_pointer,
- optional_single_const_pointer,
- error_union,
- anyframe_T,
- error_set,
- error_set_single,
-
- pub const last_no_payload_tag = Tag.const_slice_u8;
- pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
- };
-
- pub const Payload = struct {
- tag: Tag,
-
- pub const Array_u8_Sentinel0 = struct {
- base: Payload = Payload{ .tag = .array_u8_sentinel_0 },
-
- len: u64,
- };
-
- pub const Array_u8 = struct {
- base: Payload = Payload{ .tag = .array_u8 },
-
- len: u64,
- };
-
- pub const Array = struct {
- base: Payload = Payload{ .tag = .array },
-
- len: u64,
- elem_type: Type,
- };
-
- pub const ArraySentinel = struct {
- base: Payload = Payload{ .tag = .array_sentinel },
-
- len: u64,
- sentinel: Value,
- elem_type: Type,
- };
-
- pub const PointerSimple = struct {
- base: Payload,
-
- pointee_type: Type,
- };
-
- pub const IntSigned = struct {
- base: Payload = Payload{ .tag = .int_signed },
-
- bits: u16,
- };
-
- pub const IntUnsigned = struct {
- base: Payload = Payload{ .tag = .int_unsigned },
-
- bits: u16,
- };
-
- pub const Function = struct {
- base: Payload = Payload{ .tag = .function },
-
- param_types: []Type,
- return_type: Type,
- cc: std.builtin.CallingConvention,
- };
-
- pub const Optional = struct {
- base: Payload = Payload{ .tag = .optional },
-
- child_type: Type,
- };
-
- pub const Pointer = struct {
- base: Payload = .{ .tag = .pointer },
-
- pointee_type: Type,
- sentinel: ?Value,
- /// If zero use pointee_type.AbiAlign()
- @"align": u32,
- bit_offset: u16,
- host_size: u16,
- @"allowzero": bool,
- mutable: bool,
- @"volatile": bool,
- size: std.builtin.TypeInfo.Pointer.Size,
- };
-
- pub const ErrorUnion = struct {
- base: Payload = .{ .tag = .error_union },
-
- error_set: Type,
- payload: Type,
- };
-
- pub const AnyFrame = struct {
- base: Payload = .{ .tag = .anyframe_T },
-
- return_type: Type,
- };
-
- pub const ErrorSet = struct {
- base: Payload = .{ .tag = .error_set },
-
- decl: *Module.Decl,
- };
-
- pub const ErrorSetSingle = struct {
- base: Payload = .{ .tag = .error_set_single },
-
- /// memory is owned by `Module`
- name: []const u8,
- };
- };
-};
-
-pub const CType = enum {
- short,
- ushort,
- int,
- uint,
- long,
- ulong,
- longlong,
- ulonglong,
- longdouble,
-
- pub fn sizeInBits(self: CType, target: Target) u16 {
- const arch = target.cpu.arch;
- switch (target.os.tag) {
- .freestanding, .other => switch (target.cpu.arch) {
- .msp430 => switch (self) {
- .short,
- .ushort,
- .int,
- .uint,
- => return 16,
- .long,
- .ulong,
- => return 32,
- .longlong,
- .ulonglong,
- => return 64,
- .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
- },
- else => switch (self) {
- .short,
- .ushort,
- => return 16,
- .int,
- .uint,
- => return 32,
- .long,
- .ulong,
- => return target.cpu.arch.ptrBitWidth(),
- .longlong,
- .ulonglong,
- => return 64,
- .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
- },
- },
-
- .linux,
- .macosx,
- .freebsd,
- .netbsd,
- .dragonfly,
- .openbsd,
- .wasi,
- .emscripten,
- => switch (self) {
- .short,
- .ushort,
- => return 16,
- .int,
- .uint,
- => return 32,
- .long,
- .ulong,
- => return target.cpu.arch.ptrBitWidth(),
- .longlong,
- .ulonglong,
- => return 64,
- .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
- },
-
- .windows, .uefi => switch (self) {
- .short,
- .ushort,
- => return 16,
- .int,
- .uint,
- .long,
- .ulong,
- => return 32,
- .longlong,
- .ulonglong,
- => return 64,
- .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
- },
-
- .ios => switch (self) {
- .short,
- .ushort,
- => return 16,
- .int,
- .uint,
- => return 32,
- .long,
- .ulong,
- .longlong,
- .ulonglong,
- => return 64,
- .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
- },
-
- .ananas,
- .cloudabi,
- .fuchsia,
- .kfreebsd,
- .lv2,
- .solaris,
- .haiku,
- .minix,
- .rtems,
- .nacl,
- .cnk,
- .aix,
- .cuda,
- .nvcl,
- .amdhsa,
- .ps4,
- .elfiamcu,
- .tvos,
- .watchos,
- .mesa3d,
- .contiki,
- .amdpal,
- .hermit,
- .hurd,
- => @panic("TODO specify the C integer and float type sizes for this OS"),
- }
- }
-};
diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig
deleted file mode 100644
index b65aa06beaa0409ccf199b2cbd805fa5a48548d6..0000000000000000000000000000000000000000
--- a/src-self-hosted/value.zig
+++ /dev/null
@@ -1,1641 +0,0 @@
-const std = @import("std");
-const Type = @import("type.zig").Type;
-const log2 = std.math.log2;
-const assert = std.debug.assert;
-const BigIntConst = std.math.big.int.Const;
-const BigIntMutable = std.math.big.int.Mutable;
-const Target = std.Target;
-const Allocator = std.mem.Allocator;
-const Module = @import("Module.zig");
-
-/// This is the raw data, with no bookkeeping, no memory awareness,
-/// no de-duplication, and no type system awareness.
-/// It's important for this type to be small.
-/// This union takes advantage of the fact that the first page of memory
-/// is unmapped, giving us 4096 possible enum tags that have no payload.
-pub const Value = extern union {
- /// If the tag value is less than Tag.no_payload_count, then no pointer
- /// dereference is needed.
- tag_if_small_enough: usize,
- ptr_otherwise: *Payload,
-
- pub const Tag = enum {
- // The first section of this enum are tags that require no payload.
- u8_type,
- i8_type,
- u16_type,
- i16_type,
- u32_type,
- i32_type,
- u64_type,
- i64_type,
- usize_type,
- isize_type,
- c_short_type,
- c_ushort_type,
- c_int_type,
- c_uint_type,
- c_long_type,
- c_ulong_type,
- c_longlong_type,
- c_ulonglong_type,
- c_longdouble_type,
- f16_type,
- f32_type,
- f64_type,
- f128_type,
- c_void_type,
- bool_type,
- void_type,
- type_type,
- anyerror_type,
- comptime_int_type,
- comptime_float_type,
- noreturn_type,
- null_type,
- undefined_type,
- fn_noreturn_no_args_type,
- fn_void_no_args_type,
- fn_naked_noreturn_no_args_type,
- fn_ccc_void_no_args_type,
- single_const_pointer_to_comptime_int_type,
- const_slice_u8_type,
- enum_literal_type,
- anyframe_type,
-
- undef,
- zero,
- one,
- void_value,
- unreachable_value,
- empty_array,
- null_value,
- bool_true,
- bool_false, // See last_no_payload_tag below.
- // After this, the tag requires a payload.
-
- ty,
- int_type,
- int_u64,
- int_i64,
- int_big_positive,
- int_big_negative,
- function,
- variable,
- ref_val,
- decl_ref,
- elem_ptr,
- bytes,
- repeated, // the value is a value repeated some number of times
- float_16,
- float_32,
- float_64,
- float_128,
- enum_literal,
- error_set,
- @"error",
-
- pub const last_no_payload_tag = Tag.bool_false;
- pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
- };
-
- pub fn initTag(small_tag: Tag) Value {
- assert(@enumToInt(small_tag) < Tag.no_payload_count);
- return .{ .tag_if_small_enough = @enumToInt(small_tag) };
- }
-
- pub fn initPayload(payload: *Payload) Value {
- assert(@enumToInt(payload.tag) >= Tag.no_payload_count);
- return .{ .ptr_otherwise = payload };
- }
-
- pub fn tag(self: Value) Tag {
- if (self.tag_if_small_enough < Tag.no_payload_count) {
- return @intToEnum(Tag, @intCast(@TagType(Tag), self.tag_if_small_enough));
- } else {
- return self.ptr_otherwise.tag;
- }
- }
-
- pub fn cast(self: Value, comptime T: type) ?*T {
- if (self.tag_if_small_enough < Tag.no_payload_count)
- return null;
-
- const expected_tag = std.meta.fieldInfo(T, "base").default_value.?.tag;
- if (self.ptr_otherwise.tag != expected_tag)
- return null;
-
- return @fieldParentPtr(T, "base", self.ptr_otherwise);
- }
-
- pub fn copy(self: Value, allocator: *Allocator) error{OutOfMemory}!Value {
- if (self.tag_if_small_enough < Tag.no_payload_count) {
- return Value{ .tag_if_small_enough = self.tag_if_small_enough };
- } else switch (self.ptr_otherwise.tag) {
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .undef,
- .zero,
- .one,
- .void_value,
- .unreachable_value,
- .empty_array,
- .null_value,
- .bool_true,
- .bool_false,
- => unreachable,
-
- .ty => {
- const payload = @fieldParentPtr(Payload.Ty, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(Payload.Ty);
- new_payload.* = .{
- .base = payload.base,
- .ty = try payload.ty.copy(allocator),
- };
- return Value{ .ptr_otherwise = &new_payload.base };
- },
- .int_type => return self.copyPayloadShallow(allocator, Payload.IntType),
- .int_u64 => return self.copyPayloadShallow(allocator, Payload.Int_u64),
- .int_i64 => return self.copyPayloadShallow(allocator, Payload.Int_i64),
- .int_big_positive => {
- @panic("TODO implement copying of big ints");
- },
- .int_big_negative => {
- @panic("TODO implement copying of big ints");
- },
- .function => return self.copyPayloadShallow(allocator, Payload.Function),
- .variable => return self.copyPayloadShallow(allocator, Payload.Variable),
- .ref_val => {
- const payload = @fieldParentPtr(Payload.RefVal, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(Payload.RefVal);
- new_payload.* = .{
- .base = payload.base,
- .val = try payload.val.copy(allocator),
- };
- return Value{ .ptr_otherwise = &new_payload.base };
- },
- .decl_ref => return self.copyPayloadShallow(allocator, Payload.DeclRef),
- .elem_ptr => {
- const payload = @fieldParentPtr(Payload.ElemPtr, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(Payload.ElemPtr);
- new_payload.* = .{
- .base = payload.base,
- .array_ptr = try payload.array_ptr.copy(allocator),
- .index = payload.index,
- };
- return Value{ .ptr_otherwise = &new_payload.base };
- },
- .bytes => return self.copyPayloadShallow(allocator, Payload.Bytes),
- .repeated => {
- const payload = @fieldParentPtr(Payload.Repeated, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(Payload.Repeated);
- new_payload.* = .{
- .base = payload.base,
- .val = try payload.val.copy(allocator),
- };
- return Value{ .ptr_otherwise = &new_payload.base };
- },
- .float_16 => return self.copyPayloadShallow(allocator, Payload.Float_16),
- .float_32 => return self.copyPayloadShallow(allocator, Payload.Float_32),
- .float_64 => return self.copyPayloadShallow(allocator, Payload.Float_64),
- .float_128 => return self.copyPayloadShallow(allocator, Payload.Float_128),
- .enum_literal => {
- const payload = @fieldParentPtr(Payload.Bytes, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(Payload.Bytes);
- new_payload.* = .{
- .base = payload.base,
- .data = try allocator.dupe(u8, payload.data),
- };
- return Value{ .ptr_otherwise = &new_payload.base };
- },
- .@"error" => return self.copyPayloadShallow(allocator, Payload.Error),
-
- // memory is managed by the declaration
- .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
- }
- }
-
- fn copyPayloadShallow(self: Value, allocator: *Allocator, comptime T: type) error{OutOfMemory}!Value {
- const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(T);
- new_payload.* = payload.*;
- return Value{ .ptr_otherwise = &new_payload.base };
- }
-
- pub fn format(
- self: Value,
- comptime fmt: []const u8,
- options: std.fmt.FormatOptions,
- out_stream: anytype,
- ) !void {
- comptime assert(fmt.len == 0);
- var val = self;
- while (true) switch (val.tag()) {
- .u8_type => return out_stream.writeAll("u8"),
- .i8_type => return out_stream.writeAll("i8"),
- .u16_type => return out_stream.writeAll("u16"),
- .i16_type => return out_stream.writeAll("i16"),
- .u32_type => return out_stream.writeAll("u32"),
- .i32_type => return out_stream.writeAll("i32"),
- .u64_type => return out_stream.writeAll("u64"),
- .i64_type => return out_stream.writeAll("i64"),
- .isize_type => return out_stream.writeAll("isize"),
- .usize_type => return out_stream.writeAll("usize"),
- .c_short_type => return out_stream.writeAll("c_short"),
- .c_ushort_type => return out_stream.writeAll("c_ushort"),
- .c_int_type => return out_stream.writeAll("c_int"),
- .c_uint_type => return out_stream.writeAll("c_uint"),
- .c_long_type => return out_stream.writeAll("c_long"),
- .c_ulong_type => return out_stream.writeAll("c_ulong"),
- .c_longlong_type => return out_stream.writeAll("c_longlong"),
- .c_ulonglong_type => return out_stream.writeAll("c_ulonglong"),
- .c_longdouble_type => return out_stream.writeAll("c_longdouble"),
- .f16_type => return out_stream.writeAll("f16"),
- .f32_type => return out_stream.writeAll("f32"),
- .f64_type => return out_stream.writeAll("f64"),
- .f128_type => return out_stream.writeAll("f128"),
- .c_void_type => return out_stream.writeAll("c_void"),
- .bool_type => return out_stream.writeAll("bool"),
- .void_type => return out_stream.writeAll("void"),
- .type_type => return out_stream.writeAll("type"),
- .anyerror_type => return out_stream.writeAll("anyerror"),
- .comptime_int_type => return out_stream.writeAll("comptime_int"),
- .comptime_float_type => return out_stream.writeAll("comptime_float"),
- .noreturn_type => return out_stream.writeAll("noreturn"),
- .null_type => return out_stream.writeAll("@Type(.Null)"),
- .undefined_type => return out_stream.writeAll("@Type(.Undefined)"),
- .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"),
- .fn_void_no_args_type => return out_stream.writeAll("fn() void"),
- .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
- .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),
- .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
- .const_slice_u8_type => return out_stream.writeAll("[]const u8"),
- .enum_literal_type => return out_stream.writeAll("@Type(.EnumLiteral)"),
- .anyframe_type => return out_stream.writeAll("anyframe"),
-
- .null_value => return out_stream.writeAll("null"),
- .undef => return out_stream.writeAll("undefined"),
- .zero => return out_stream.writeAll("0"),
- .one => return out_stream.writeAll("1"),
- .void_value => return out_stream.writeAll("{}"),
- .unreachable_value => return out_stream.writeAll("unreachable"),
- .bool_true => return out_stream.writeAll("true"),
- .bool_false => return out_stream.writeAll("false"),
- .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream),
- .int_type => {
- const int_type = val.cast(Payload.IntType).?;
- return out_stream.print("{}{}", .{
- if (int_type.signed) "s" else "u",
- int_type.bits,
- });
- },
- .int_u64 => return std.fmt.formatIntValue(val.cast(Payload.Int_u64).?.int, "", options, out_stream),
- .int_i64 => return std.fmt.formatIntValue(val.cast(Payload.Int_i64).?.int, "", options, out_stream),
- .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}),
- .int_big_negative => return out_stream.print("{}", .{val.cast(Payload.IntBigNegative).?.asBigInt()}),
- .function => return out_stream.writeAll("(function)"),
- .variable => return out_stream.writeAll("(variable)"),
- .ref_val => {
- const ref_val = val.cast(Payload.RefVal).?;
- try out_stream.writeAll("&const ");
- val = ref_val.val;
- },
- .decl_ref => return out_stream.writeAll("(decl ref)"),
- .elem_ptr => {
- const elem_ptr = val.cast(Payload.ElemPtr).?;
- try out_stream.print("&[{}] ", .{elem_ptr.index});
- val = elem_ptr.array_ptr;
- },
- .empty_array => return out_stream.writeAll(".{}"),
- .enum_literal, .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream),
- .repeated => {
- try out_stream.writeAll("(repeated) ");
- val = val.cast(Payload.Repeated).?.val;
- },
- .float_16 => return out_stream.print("{}", .{val.cast(Payload.Float_16).?.val}),
- .float_32 => return out_stream.print("{}", .{val.cast(Payload.Float_32).?.val}),
- .float_64 => return out_stream.print("{}", .{val.cast(Payload.Float_64).?.val}),
- .float_128 => return out_stream.print("{}", .{val.cast(Payload.Float_128).?.val}),
- .error_set => {
- const error_set = val.cast(Payload.ErrorSet).?;
- try out_stream.writeAll("error{");
- var it = error_set.fields.iterator();
- while (it.next()) |entry| {
- try out_stream.print("{},", .{entry.value});
- }
- return out_stream.writeAll("}");
- },
- .@"error" => return out_stream.print("error.{}", .{val.cast(Payload.Error).?.name}),
- };
- }
-
- /// Asserts that the value is representable as an array of bytes.
- /// Copies the value into a freshly allocated slice of memory, which is owned by the caller.
- pub fn toAllocatedBytes(self: Value, allocator: *Allocator) ![]u8 {
- if (self.cast(Payload.Bytes)) |bytes| {
- return std.mem.dupe(allocator, u8, bytes.data);
- }
- if (self.cast(Payload.Repeated)) |repeated| {
- @panic("TODO implement toAllocatedBytes for this Value tag");
- }
- if (self.cast(Payload.DeclRef)) |declref| {
- const val = try declref.decl.value();
- return val.toAllocatedBytes(allocator);
- }
- unreachable;
- }
-
- /// Asserts that the value is representable as a type.
- pub fn toType(self: Value, allocator: *Allocator) !Type {
- return switch (self.tag()) {
- .ty => self.cast(Payload.Ty).?.ty,
- .u8_type => Type.initTag(.u8),
- .i8_type => Type.initTag(.i8),
- .u16_type => Type.initTag(.u16),
- .i16_type => Type.initTag(.i16),
- .u32_type => Type.initTag(.u32),
- .i32_type => Type.initTag(.i32),
- .u64_type => Type.initTag(.u64),
- .i64_type => Type.initTag(.i64),
- .usize_type => Type.initTag(.usize),
- .isize_type => Type.initTag(.isize),
- .c_short_type => Type.initTag(.c_short),
- .c_ushort_type => Type.initTag(.c_ushort),
- .c_int_type => Type.initTag(.c_int),
- .c_uint_type => Type.initTag(.c_uint),
- .c_long_type => Type.initTag(.c_long),
- .c_ulong_type => Type.initTag(.c_ulong),
- .c_longlong_type => Type.initTag(.c_longlong),
- .c_ulonglong_type => Type.initTag(.c_ulonglong),
- .c_longdouble_type => Type.initTag(.c_longdouble),
- .f16_type => Type.initTag(.f16),
- .f32_type => Type.initTag(.f32),
- .f64_type => Type.initTag(.f64),
- .f128_type => Type.initTag(.f128),
- .c_void_type => Type.initTag(.c_void),
- .bool_type => Type.initTag(.bool),
- .void_type => Type.initTag(.void),
- .type_type => Type.initTag(.type),
- .anyerror_type => Type.initTag(.anyerror),
- .comptime_int_type => Type.initTag(.comptime_int),
- .comptime_float_type => Type.initTag(.comptime_float),
- .noreturn_type => Type.initTag(.noreturn),
- .null_type => Type.initTag(.@"null"),
- .undefined_type => Type.initTag(.@"undefined"),
- .fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args),
- .fn_void_no_args_type => Type.initTag(.fn_void_no_args),
- .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),
- .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args),
- .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
- .const_slice_u8_type => Type.initTag(.const_slice_u8),
- .enum_literal_type => Type.initTag(.enum_literal),
- .anyframe_type => Type.initTag(.@"anyframe"),
-
- .int_type => {
- const payload = self.cast(Payload.IntType).?;
- if (payload.signed) {
- const new = try allocator.create(Type.Payload.IntSigned);
- new.* = .{ .bits = payload.bits };
- return Type.initPayload(&new.base);
- } else {
- const new = try allocator.create(Type.Payload.IntUnsigned);
- new.* = .{ .bits = payload.bits };
- return Type.initPayload(&new.base);
- }
- },
- .error_set => {
- const payload = self.cast(Payload.ErrorSet).?;
- const new = try allocator.create(Type.Payload.ErrorSet);
- new.* = .{ .decl = payload.decl };
- return Type.initPayload(&new.base);
- },
-
- .undef,
- .zero,
- .one,
- .void_value,
- .unreachable_value,
- .empty_array,
- .bool_true,
- .bool_false,
- .null_value,
- .int_u64,
- .int_i64,
- .int_big_positive,
- .int_big_negative,
- .function,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .enum_literal,
- .@"error",
- => unreachable,
- };
- }
-
- /// Asserts the value is an integer.
- pub fn toBigInt(self: Value, space: *BigIntSpace) BigIntConst {
- switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .null_value,
- .function,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .unreachable_value,
- .empty_array,
- .enum_literal,
- .error_set,
- .@"error",
- => unreachable,
-
- .undef => unreachable,
-
- .zero,
- .bool_false,
- => return BigIntMutable.init(&space.limbs, 0).toConst(),
-
- .one,
- .bool_true,
- => return BigIntMutable.init(&space.limbs, 1).toConst(),
-
- .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(),
- .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(),
- .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(),
- .int_big_negative => return self.cast(Payload.IntBigPositive).?.asBigInt(),
- }
- }
-
- /// Asserts the value is an integer and it fits in a u64
- pub fn toUnsignedInt(self: Value) u64 {
- switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .null_value,
- .function,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .unreachable_value,
- .empty_array,
- .enum_literal,
- .error_set,
- .@"error",
- => unreachable,
-
- .undef => unreachable,
-
- .zero,
- .bool_false,
- => return 0,
-
- .one,
- .bool_true,
- => return 1,
-
- .int_u64 => return self.cast(Payload.Int_u64).?.int,
- .int_i64 => return @intCast(u64, self.cast(Payload.Int_i64).?.int),
- .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable,
- .int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().to(u64) catch unreachable,
- }
- }
-
- /// Asserts the value is an integer and it fits in a i64
- pub fn toSignedInt(self: Value) i64 {
- switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .null_value,
- .function,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .unreachable_value,
- .empty_array,
- .enum_literal,
- .error_set,
- .@"error",
- => unreachable,
-
- .undef => unreachable,
-
- .zero,
- .bool_false,
- => return 0,
-
- .one,
- .bool_true,
- => return 1,
-
- .int_u64 => return @intCast(i64, self.cast(Payload.Int_u64).?.int),
- .int_i64 => return self.cast(Payload.Int_i64).?.int,
- .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(i64) catch unreachable,
- .int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().to(i64) catch unreachable,
- }
- }
-
- pub fn toBool(self: Value) bool {
- return switch (self.tag()) {
- .bool_true => true,
- .bool_false, .zero => false,
- else => unreachable,
- };
- }
-
- /// Asserts that the value is a float or an integer.
- pub fn toFloat(self: Value, comptime T: type) T {
- return switch (self.tag()) {
- .float_16 => @panic("TODO soft float"),
- .float_32 => @floatCast(T, self.cast(Payload.Float_32).?.val),
- .float_64 => @floatCast(T, self.cast(Payload.Float_64).?.val),
- .float_128 => @floatCast(T, self.cast(Payload.Float_128).?.val),
-
- .zero => 0,
- .one => 1,
- .int_u64 => @intToFloat(T, self.cast(Payload.Int_u64).?.int),
- .int_i64 => @intToFloat(T, self.cast(Payload.Int_i64).?.int),
-
- .int_big_positive, .int_big_negative => @panic("big int to f128"),
- else => unreachable,
- };
- }
-
- /// Asserts the value is an integer and not undefined.
- /// Returns the number of bits the value requires to represent stored in twos complement form.
- pub fn intBitCountTwosComp(self: Value) usize {
- switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .null_value,
- .function,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .undef,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .unreachable_value,
- .empty_array,
- .enum_literal,
- .error_set,
- .@"error",
- => unreachable,
-
- .zero,
- .bool_false,
- => return 0,
-
- .one,
- .bool_true,
- => return 1,
-
- .int_u64 => {
- const x = self.cast(Payload.Int_u64).?.int;
- if (x == 0) return 0;
- return std.math.log2(x) + 1;
- },
- .int_i64 => {
- @panic("TODO implement i64 intBitCountTwosComp");
- },
- .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().bitCountTwosComp(),
- .int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().bitCountTwosComp(),
- }
- }
-
- /// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
- pub fn intFitsInType(self: Value, ty: Type, target: Target) bool {
- switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .null_value,
- .function,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .unreachable_value,
- .empty_array,
- .enum_literal,
- .error_set,
- .@"error",
- => unreachable,
-
- .zero,
- .undef,
- .bool_false,
- => return true,
-
- .one,
- .bool_true,
- => {
- const info = ty.intInfo(target);
- if (info.signed) {
- return info.bits >= 2;
- } else {
- return info.bits >= 1;
- }
- },
-
- .int_u64 => switch (ty.zigTypeTag()) {
- .Int => {
- const x = self.cast(Payload.Int_u64).?.int;
- if (x == 0) return true;
- const info = ty.intInfo(target);
- const needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signed);
- return info.bits >= needed_bits;
- },
- .ComptimeInt => return true,
- else => unreachable,
- },
- .int_i64 => switch (ty.zigTypeTag()) {
- .Int => {
- const x = self.cast(Payload.Int_i64).?.int;
- if (x == 0) return true;
- const info = ty.intInfo(target);
- if (!info.signed and x < 0)
- return false;
- @panic("TODO implement i64 intFitsInType");
- },
- .ComptimeInt => return true,
- else => unreachable,
- },
- .int_big_positive => switch (ty.zigTypeTag()) {
- .Int => {
- const info = ty.intInfo(target);
- return self.cast(Payload.IntBigPositive).?.asBigInt().fitsInTwosComp(info.signed, info.bits);
- },
- .ComptimeInt => return true,
- else => unreachable,
- },
- .int_big_negative => switch (ty.zigTypeTag()) {
- .Int => {
- const info = ty.intInfo(target);
- return self.cast(Payload.IntBigNegative).?.asBigInt().fitsInTwosComp(info.signed, info.bits);
- },
- .ComptimeInt => return true,
- else => unreachable,
- },
- }
- }
-
- /// Converts an integer or a float to a float.
- /// Returns `error.Overflow` if the value does not fit in the new type.
- pub fn floatCast(self: Value, allocator: *Allocator, ty: Type, target: Target) !Value {
- const dest_bit_count = switch (ty.tag()) {
- .comptime_float => 128,
- else => ty.floatBits(target),
- };
- switch (dest_bit_count) {
- 16, 32, 64, 128 => {},
- else => std.debug.panic("TODO float cast bit count {}\n", .{dest_bit_count}),
- }
- if (ty.isInt()) {
- @panic("TODO int to float");
- }
-
- switch (dest_bit_count) {
- 16 => {
- @panic("TODO soft float");
- // var res_payload = Value.Payload.Float_16{.val = self.toFloat(f16)};
- // if (!self.eql(Value.initPayload(&res_payload.base)))
- // return error.Overflow;
- // return Value.initPayload(&res_payload.base).copy(allocator);
- },
- 32 => {
- var res_payload = Value.Payload.Float_32{ .val = self.toFloat(f32) };
- if (!self.eql(Value.initPayload(&res_payload.base)))
- return error.Overflow;
- return Value.initPayload(&res_payload.base).copy(allocator);
- },
- 64 => {
- var res_payload = Value.Payload.Float_64{ .val = self.toFloat(f64) };
- if (!self.eql(Value.initPayload(&res_payload.base)))
- return error.Overflow;
- return Value.initPayload(&res_payload.base).copy(allocator);
- },
- 128 => {
- const float_payload = try allocator.create(Value.Payload.Float_128);
- float_payload.* = .{ .val = self.toFloat(f128) };
- return Value.initPayload(&float_payload.base);
- },
- else => unreachable,
- }
- }
-
- /// Asserts the value is a float
- pub fn floatHasFraction(self: Value) bool {
- return switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .bool_true,
- .bool_false,
- .null_value,
- .function,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .undef,
- .int_u64,
- .int_i64,
- .int_big_positive,
- .int_big_negative,
- .empty_array,
- .void_value,
- .unreachable_value,
- .enum_literal,
- .error_set,
- .@"error",
- => unreachable,
-
- .zero,
- .one,
- => false,
-
- .float_16 => @rem(self.cast(Payload.Float_16).?.val, 1) != 0,
- .float_32 => @rem(self.cast(Payload.Float_32).?.val, 1) != 0,
- .float_64 => @rem(self.cast(Payload.Float_64).?.val, 1) != 0,
- // .float_128 => @rem(self.cast(Payload.Float_128).?.val, 1) != 0,
- .float_128 => @panic("TODO lld: error: undefined symbol: fmodl"),
- };
- }
-
- pub fn orderAgainstZero(lhs: Value) std.math.Order {
- return switch (lhs.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .null_value,
- .function,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .undef,
- .void_value,
- .unreachable_value,
- .empty_array,
- .enum_literal,
- .error_set,
- .@"error",
- => unreachable,
-
- .zero,
- .bool_false,
- => .eq,
-
- .one,
- .bool_true,
- => .gt,
-
- .int_u64 => std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),
- .int_i64 => std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),
- .int_big_positive => lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0),
- .int_big_negative => lhs.cast(Payload.IntBigNegative).?.asBigInt().orderAgainstScalar(0),
-
- .float_16 => std.math.order(lhs.cast(Payload.Float_16).?.val, 0),
- .float_32 => std.math.order(lhs.cast(Payload.Float_32).?.val, 0),
- .float_64 => std.math.order(lhs.cast(Payload.Float_64).?.val, 0),
- .float_128 => std.math.order(lhs.cast(Payload.Float_128).?.val, 0),
- };
- }
-
- /// Asserts the value is comparable.
- pub fn order(lhs: Value, rhs: Value) std.math.Order {
- const lhs_tag = lhs.tag();
- const rhs_tag = rhs.tag();
- const lhs_is_zero = lhs_tag == .zero;
- const rhs_is_zero = rhs_tag == .zero;
- if (lhs_is_zero) return rhs.orderAgainstZero().invert();
- if (rhs_is_zero) return lhs.orderAgainstZero();
-
- const lhs_float = lhs.isFloat();
- const rhs_float = rhs.isFloat();
- if (lhs_float and rhs_float) {
- if (lhs_tag == rhs_tag) {
- return switch (lhs.tag()) {
- .float_16 => return std.math.order(lhs.cast(Payload.Float_16).?.val, rhs.cast(Payload.Float_16).?.val),
- .float_32 => return std.math.order(lhs.cast(Payload.Float_32).?.val, rhs.cast(Payload.Float_32).?.val),
- .float_64 => return std.math.order(lhs.cast(Payload.Float_64).?.val, rhs.cast(Payload.Float_64).?.val),
- .float_128 => return std.math.order(lhs.cast(Payload.Float_128).?.val, rhs.cast(Payload.Float_128).?.val),
- else => unreachable,
- };
- }
- }
- if (lhs_float or rhs_float) {
- const lhs_f128 = lhs.toFloat(f128);
- const rhs_f128 = rhs.toFloat(f128);
- return std.math.order(lhs_f128, rhs_f128);
- }
-
- var lhs_bigint_space: BigIntSpace = undefined;
- var rhs_bigint_space: BigIntSpace = undefined;
- const lhs_bigint = lhs.toBigInt(&lhs_bigint_space);
- const rhs_bigint = rhs.toBigInt(&rhs_bigint_space);
- return lhs_bigint.order(rhs_bigint);
- }
-
- /// Asserts the value is comparable.
- pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value) bool {
- return order(lhs, rhs).compare(op);
- }
-
- /// Asserts the value is comparable.
- pub fn compareWithZero(lhs: Value, op: std.math.CompareOperator) bool {
- return orderAgainstZero(lhs).compare(op);
- }
-
- pub fn eql(a: Value, b: Value) bool {
- if (a.tag() == b.tag() and a.tag() == .enum_literal) {
- const a_name = @fieldParentPtr(Payload.Bytes, "base", a.ptr_otherwise).data;
- const b_name = @fieldParentPtr(Payload.Bytes, "base", b.ptr_otherwise).data;
- return std.mem.eql(u8, a_name, b_name);
- }
- // TODO non numerical comparisons
- return compare(a, .eq, b);
- }
-
- /// Asserts the value is a pointer and dereferences it.
- /// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis.
- pub fn pointerDeref(self: Value, allocator: *Allocator) error{ AnalysisFail, OutOfMemory }!Value {
- return switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .zero,
- .one,
- .bool_true,
- .bool_false,
- .null_value,
- .function,
- .variable,
- .int_u64,
- .int_i64,
- .int_big_positive,
- .int_big_negative,
- .bytes,
- .undef,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .unreachable_value,
- .empty_array,
- .enum_literal,
- .error_set,
- .@"error",
- => unreachable,
-
- .ref_val => self.cast(Payload.RefVal).?.val,
- .decl_ref => self.cast(Payload.DeclRef).?.decl.value(),
- .elem_ptr => {
- const elem_ptr = self.cast(Payload.ElemPtr).?;
- const array_val = try elem_ptr.array_ptr.pointerDeref(allocator);
- return array_val.elemValue(allocator, elem_ptr.index);
- },
- };
- }
-
- /// Asserts the value is a single-item pointer to an array, or an array,
- /// or an unknown-length pointer, and returns the element value at the index.
- pub fn elemValue(self: Value, allocator: *Allocator, index: usize) error{OutOfMemory}!Value {
- switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .zero,
- .one,
- .bool_true,
- .bool_false,
- .null_value,
- .function,
- .variable,
- .int_u64,
- .int_i64,
- .int_big_positive,
- .int_big_negative,
- .undef,
- .elem_ptr,
- .ref_val,
- .decl_ref,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .unreachable_value,
- .enum_literal,
- .error_set,
- .@"error",
- => unreachable,
-
- .empty_array => unreachable, // out of bounds array index
-
- .bytes => {
- const int_payload = try allocator.create(Payload.Int_u64);
- int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] };
- return Value.initPayload(&int_payload.base);
- },
-
- // No matter the index; all the elements are the same!
- .repeated => return self.cast(Payload.Repeated).?.val,
- }
- }
-
- /// Returns a pointer to the element value at the index.
- pub fn elemPtr(self: Value, allocator: *Allocator, index: usize) !Value {
- const payload = try allocator.create(Payload.ElemPtr);
- if (self.cast(Payload.ElemPtr)) |elem_ptr| {
- payload.* = .{ .array_ptr = elem_ptr.array_ptr, .index = elem_ptr.index + index };
- } else {
- payload.* = .{ .array_ptr = self, .index = index };
- }
- return Value.initPayload(&payload.base);
- }
-
- pub fn isUndef(self: Value) bool {
- return self.tag() == .undef;
- }
-
- /// Valid for all types. Asserts the value is not undefined and not unreachable.
- pub fn isNull(self: Value) bool {
- return switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .zero,
- .one,
- .empty_array,
- .bool_true,
- .bool_false,
- .function,
- .variable,
- .int_u64,
- .int_i64,
- .int_big_positive,
- .int_big_negative,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .enum_literal,
- .error_set,
- .@"error",
- => false,
-
- .undef => unreachable,
- .unreachable_value => unreachable,
- .null_value => true,
- };
- }
-
- /// Valid for all types. Asserts the value is not undefined.
- pub fn isFloat(self: Value) bool {
- return switch (self.tag()) {
- .undef => unreachable,
-
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- => true,
- else => false,
- };
- }
-
- /// This type is not copyable since it may contain pointers to its inner data.
- pub const Payload = struct {
- tag: Tag,
-
- pub const Int_u64 = struct {
- base: Payload = Payload{ .tag = .int_u64 },
- int: u64,
- };
-
- pub const Int_i64 = struct {
- base: Payload = Payload{ .tag = .int_i64 },
- int: i64,
- };
-
- pub const IntBigPositive = struct {
- base: Payload = Payload{ .tag = .int_big_positive },
- limbs: []const std.math.big.Limb,
-
- pub fn asBigInt(self: IntBigPositive) BigIntConst {
- return BigIntConst{ .limbs = self.limbs, .positive = true };
- }
- };
-
- pub const IntBigNegative = struct {
- base: Payload = Payload{ .tag = .int_big_negative },
- limbs: []const std.math.big.Limb,
-
- pub fn asBigInt(self: IntBigNegative) BigIntConst {
- return BigIntConst{ .limbs = self.limbs, .positive = false };
- }
- };
-
- pub const Function = struct {
- base: Payload = Payload{ .tag = .function },
- func: *Module.Fn,
- };
-
- pub const Variable = struct {
- base: Payload = Payload{ .tag = .variable },
- variable: *Module.Var,
- };
-
- pub const ArraySentinel0_u8_Type = struct {
- base: Payload = Payload{ .tag = .array_sentinel_0_u8_type },
- len: u64,
- };
-
- /// Represents a pointer to another immutable value.
- pub const RefVal = struct {
- base: Payload = Payload{ .tag = .ref_val },
- val: Value,
- };
-
- /// Represents a pointer to a decl, not the value of the decl.
- pub const DeclRef = struct {
- base: Payload = Payload{ .tag = .decl_ref },
- decl: *Module.Decl,
- };
-
- pub const ElemPtr = struct {
- base: Payload = Payload{ .tag = .elem_ptr },
- array_ptr: Value,
- index: usize,
- };
-
- pub const Bytes = struct {
- base: Payload = Payload{ .tag = .bytes },
- data: []const u8,
- };
-
- pub const Ty = struct {
- base: Payload = Payload{ .tag = .ty },
- ty: Type,
- };
-
- pub const IntType = struct {
- base: Payload = Payload{ .tag = .int_type },
- bits: u16,
- signed: bool,
- };
-
- pub const Repeated = struct {
- base: Payload = Payload{ .tag = .ty },
- /// This value is repeated some number of times. The amount of times to repeat
- /// is stored externally.
- val: Value,
- };
-
- pub const Float_16 = struct {
- base: Payload = .{ .tag = .float_16 },
- val: f16,
- };
-
- pub const Float_32 = struct {
- base: Payload = .{ .tag = .float_32 },
- val: f32,
- };
-
- pub const Float_64 = struct {
- base: Payload = .{ .tag = .float_64 },
- val: f64,
- };
-
- pub const Float_128 = struct {
- base: Payload = .{ .tag = .float_128 },
- val: f128,
- };
-
- pub const ErrorSet = struct {
- base: Payload = .{ .tag = .error_set },
-
- // TODO revisit this when we have the concept of the error tag type
- fields: std.StringHashMapUnmanaged(u16),
- decl: *Module.Decl,
- };
-
- pub const Error = struct {
- base: Payload = .{ .tag = .@"error" },
-
- // TODO revisit this when we have the concept of the error tag type
- /// `name` is owned by `Module` and will be valid for the entire
- /// duration of the compilation.
- name: []const u8,
- value: u16,
- };
- };
-
- /// Big enough to fit any non-BigInt value
- pub const BigIntSpace = struct {
- /// The +1 is headroom so that operations such as incrementing once or decrementing once
- /// are possible without using an allocator.
- limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb,
- };
-};
diff --git a/src-self-hosted/windows_sdk.zig b/src-self-hosted/windows_sdk.zig
deleted file mode 100644
index 6dfdeb99fd0c2d0ba5f6e7934562a3a7b047bf32..0000000000000000000000000000000000000000
--- a/src-self-hosted/windows_sdk.zig
+++ /dev/null
@@ -1,22 +0,0 @@
-// C API bindings for src/windows_sdk.h
-
-pub const ZigWindowsSDK = extern struct {
- path10_ptr: ?[*]const u8,
- path10_len: usize,
- version10_ptr: ?[*]const u8,
- version10_len: usize,
- path81_ptr: ?[*]const u8,
- path81_len: usize,
- version81_ptr: ?[*]const u8,
- version81_len: usize,
- msvc_lib_dir_ptr: ?[*]const u8,
- msvc_lib_dir_len: usize,
-};
-pub const ZigFindWindowsSdkError = extern enum {
- None,
- OutOfMemory,
- NotFound,
- PathTooLong,
-};
-pub extern fn zig_find_windows_sdk(out_sdk: **ZigWindowsSDK) ZigFindWindowsSdkError;
-pub extern fn zig_free_windows_sdk(sdk: *ZigWindowsSDK) void;
diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig
deleted file mode 100644
index b6d7fab4c5626fda00b68734f6009d5f7620f962..0000000000000000000000000000000000000000
--- a/src-self-hosted/zir.zig
+++ /dev/null
@@ -1,2701 +0,0 @@
-//! This file has to do with parsing and rendering the ZIR text format.
-
-const std = @import("std");
-const mem = std.mem;
-const Allocator = std.mem.Allocator;
-const assert = std.debug.assert;
-const BigIntConst = std.math.big.int.Const;
-const BigIntMutable = std.math.big.int.Mutable;
-const Type = @import("type.zig").Type;
-const Value = @import("value.zig").Value;
-const TypedValue = @import("TypedValue.zig");
-const ir = @import("ir.zig");
-const IrModule = @import("Module.zig");
-
-/// This struct is relevent only for the ZIR Module text format. It is not used for
-/// semantic analysis of Zig source code.
-pub const Decl = struct {
- name: []const u8,
-
- /// Hash of slice into the source of the part after the = and before the next instruction.
- contents_hash: std.zig.SrcHash,
-
- inst: *Inst,
-};
-
-/// These are instructions that correspond to the ZIR text format. See `ir.Inst` for
-/// in-memory, analyzed instructions with types and values.
-pub const Inst = struct {
- tag: Tag,
- /// Byte offset into the source.
- src: usize,
- /// Pre-allocated field for mapping ZIR text instructions to post-analysis instructions.
- analyzed_inst: ?*ir.Inst = null,
-
- /// These names are used directly as the instruction names in the text format.
- pub const Tag = enum {
- /// Arithmetic addition, asserts no integer overflow.
- add,
- /// Twos complement wrapping integer addition.
- addwrap,
- /// Allocates stack local memory. Its lifetime ends when the block ends that contains
- /// this instruction. The operand is the type of the allocated object.
- alloc,
- /// Same as `alloc` except the type is inferred.
- alloc_inferred,
- /// Create an `anyframe->T`.
- anyframe_type,
- /// Array concatenation. `a ++ b`
- array_cat,
- /// Array multiplication `a ** b`
- array_mul,
- /// Create an array type
- array_type,
- /// Create an array type with sentinel
- array_type_sentinel,
- /// Function parameter value. These must be first in a function's main block,
- /// in respective order with the parameters.
- arg,
- /// Type coercion.
- as,
- /// Inline assembly.
- @"asm",
- /// Bitwise AND. `&`
- bitand,
- /// TODO delete this instruction, it has no purpose.
- bitcast,
- /// An arbitrary typed pointer is pointer-casted to a new Pointer.
- /// The destination type is given by LHS. The cast is to be evaluated
- /// as if it were a bit-cast operation from the operand pointer element type to the
- /// provided destination type.
- bitcast_ref,
- /// A typed result location pointer is bitcasted to a new result location pointer.
- /// The new result location pointer has an inferred type.
- bitcast_result_ptr,
- /// Bitwise NOT. `~`
- bitnot,
- /// Bitwise OR. `|`
- bitor,
- /// A labeled block of code, which can return a value.
- block,
- /// A block of code, which can return a value. There are no instructions that break out of
- /// this block; it is implied that the final instruction is the result.
- block_flat,
- /// Same as `block` but additionally makes the inner instructions execute at comptime.
- block_comptime,
- /// Same as `block_flat` but additionally makes the inner instructions execute at comptime.
- block_comptime_flat,
- /// Boolean NOT. See also `bitnot`.
- boolnot,
- /// Return a value from a `Block`.
- @"break",
- breakpoint,
- /// Same as `break` but without an operand; the operand is assumed to be the void value.
- breakvoid,
- /// Function call.
- call,
- /// `<`
- cmp_lt,
- /// `<=`
- cmp_lte,
- /// `==`
- cmp_eq,
- /// `>=`
- cmp_gte,
- /// `>`
- cmp_gt,
- /// `!=`
- cmp_neq,
- /// Coerces a result location pointer to a new element type. It is evaluated "backwards"-
- /// as type coercion from the new element type to the old element type.
- /// LHS is destination element type, RHS is result pointer.
- coerce_result_ptr,
- /// This instruction does a `coerce_result_ptr` operation on a `Block`'s
- /// result location pointer, whose type is inferred by peer type resolution on the
- /// `Block`'s corresponding `break` instructions.
- coerce_result_block_ptr,
- /// Equivalent to `as(ptr_child_type(typeof(ptr)), value)`.
- coerce_to_ptr_elem,
- /// Emit an error message and fail compilation.
- compileerror,
- /// Conditional branch. Splits control flow based on a boolean condition value.
- condbr,
- /// Special case, has no textual representation.
- @"const",
- /// Declares the beginning of a statement. Used for debug info.
- dbg_stmt,
- /// Represents a pointer to a global decl by name.
- declref,
- /// Represents a pointer to a global decl by string name.
- declref_str,
- /// The syntax `@foo` is equivalent to `declval("foo")`.
- /// declval is equivalent to declref followed by deref.
- declval,
- /// Same as declval but the parameter is a `*Module.Decl` rather than a name.
- declval_in_module,
- /// Load the value from a pointer.
- deref,
- /// Arithmetic division. Asserts no integer overflow.
- div,
- /// Given a pointer to an array, slice, or pointer, returns a pointer to the element at
- /// the provided index.
- elemptr,
- /// Emits a compile error if the operand is not `void`.
- ensure_result_used,
- /// Emits a compile error if an error is ignored.
- ensure_result_non_error,
- /// Emits a compile error if operand cannot be indexed.
- ensure_indexable,
- /// Create a `E!T` type.
- error_union_type,
- /// Create an error set.
- error_set,
- /// Export the provided Decl as the provided name in the compilation's output object file.
- @"export",
- /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
- /// to the named field.
- fieldptr,
- /// Convert a larger float type to any other float type, possibly causing a loss of precision.
- floatcast,
- /// Declare a function body.
- @"fn",
- /// Returns a function type.
- fntype,
- /// Integer literal.
- int,
- /// Convert an integer value to another integer type, asserting that the destination type
- /// can hold the same mathematical value.
- intcast,
- /// Make an integer type out of signedness and bit count.
- inttype,
- /// Return a boolean false if an optional is null. `x != null`
- isnonnull,
- /// Return a boolean true if an optional is null. `x == null`
- isnull,
- /// Return a boolean true if value is an error
- iserr,
- /// A labeled block of code that loops forever. At the end of the body it is implied
- /// to repeat; no explicit "repeat" instruction terminates loop bodies.
- loop,
- /// Merge two error sets into one, `E1 || E2`.
- merge_error_sets,
- /// Ambiguously remainder division or modulus. If the computation would possibly have
- /// a different value depending on whether the operation is remainder division or modulus,
- /// a compile error is emitted. Otherwise the computation is performed.
- mod_rem,
- /// Arithmetic multiplication. Asserts no integer overflow.
- mul,
- /// Twos complement wrapping integer multiplication.
- mulwrap,
- /// Given a reference to a function and a parameter index, returns the
- /// type of the parameter. TODO what happens when the parameter is `anytype`?
- param_type,
- /// An alternative to using `const` for simple primitive values such as `true` or `u8`.
- /// TODO flatten so that each primitive has its own ZIR Inst Tag.
- primitive,
- /// Convert a pointer to a `usize` integer.
- ptrtoint,
- /// Turns an R-Value into a const L-Value. In other words, it takes a value,
- /// stores it in a memory location, and returns a const pointer to it. If the value
- /// is `comptime`, the memory location is global static constant data. Otherwise,
- /// the memory location is in the stack frame, local to the scope containing the
- /// instruction.
- ref,
- /// Obtains a pointer to the return value.
- ret_ptr,
- /// Obtains the return type of the in-scope function.
- ret_type,
- /// Sends control flow back to the function's callee. Takes an operand as the return value.
- @"return",
- /// Same as `return` but there is no operand; the operand is implicitly the void value.
- returnvoid,
- /// Integer shift-left. Zeroes are shifted in from the right hand side.
- shl,
- /// Integer shift-right. Arithmetic or logical depending on the signedness of the integer type.
- shr,
- /// Create a const pointer type with element type T. `*const T`
- single_const_ptr_type,
- /// Create a mutable pointer type with element type T. `*T`
- single_mut_ptr_type,
- /// Create a const pointer type with element type T. `[*]const T`
- many_const_ptr_type,
- /// Create a mutable pointer type with element type T. `[*]T`
- many_mut_ptr_type,
- /// Create a const pointer type with element type T. `[*c]const T`
- c_const_ptr_type,
- /// Create a mutable pointer type with element type T. `[*c]T`
- c_mut_ptr_type,
- /// Create a mutable slice type with element type T. `[]T`
- mut_slice_type,
- /// Create a const slice type with element type T. `[]T`
- const_slice_type,
- /// Create a pointer type with attributes
- ptr_type,
- /// Slice operation `array_ptr[start..end:sentinel]`
- slice,
- /// Slice operation with just start `lhs[rhs..]`
- slice_start,
- /// Write a value to a pointer. For loading, see `deref`.
- store,
- /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
- str,
- /// Arithmetic subtraction. Asserts no integer overflow.
- sub,
- /// Twos complement wrapping integer subtraction.
- subwrap,
- /// Returns the type of a value.
- typeof,
- /// Asserts control-flow will not reach this instruction. Not safety checked - the compiler
- /// will assume the correctness of this instruction.
- unreach_nocheck,
- /// Asserts control-flow will not reach this instruction. In safety-checked modes,
- /// this will generate a call to the panic function unless it can be proven unreachable
- /// by the compiler.
- @"unreachable",
- /// Bitwise XOR. `^`
- xor,
- /// Create an optional type '?T'
- optional_type,
- /// Unwraps an optional value 'lhs.?'
- unwrap_optional_safe,
- /// Same as previous, but without safety checks. Used for orelse, if and while
- unwrap_optional_unsafe,
- /// Gets the payload of an error union
- unwrap_err_safe,
- /// Same as previous, but without safety checks. Used for orelse, if and while
- unwrap_err_unsafe,
- /// Gets the error code value of an error union
- unwrap_err_code,
- /// Takes a *E!T and raises a compiler error if T != void
- ensure_err_payload_void,
- /// Enum literal
- enum_literal,
-
- pub fn Type(tag: Tag) type {
- return switch (tag) {
- .breakpoint,
- .dbg_stmt,
- .returnvoid,
- .alloc_inferred,
- .ret_ptr,
- .ret_type,
- .unreach_nocheck,
- .@"unreachable",
- => NoOp,
-
- .boolnot,
- .deref,
- .@"return",
- .isnull,
- .isnonnull,
- .iserr,
- .ptrtoint,
- .alloc,
- .ensure_result_used,
- .ensure_result_non_error,
- .ensure_indexable,
- .bitcast_result_ptr,
- .ref,
- .bitcast_ref,
- .typeof,
- .single_const_ptr_type,
- .single_mut_ptr_type,
- .many_const_ptr_type,
- .many_mut_ptr_type,
- .c_const_ptr_type,
- .c_mut_ptr_type,
- .mut_slice_type,
- .const_slice_type,
- .optional_type,
- .unwrap_optional_safe,
- .unwrap_optional_unsafe,
- .unwrap_err_safe,
- .unwrap_err_unsafe,
- .unwrap_err_code,
- .ensure_err_payload_void,
- .anyframe_type,
- .bitnot,
- => UnOp,
-
- .add,
- .addwrap,
- .array_cat,
- .array_mul,
- .array_type,
- .bitand,
- .bitor,
- .div,
- .mod_rem,
- .mul,
- .mulwrap,
- .shl,
- .shr,
- .store,
- .sub,
- .subwrap,
- .cmp_lt,
- .cmp_lte,
- .cmp_eq,
- .cmp_gte,
- .cmp_gt,
- .cmp_neq,
- .as,
- .floatcast,
- .intcast,
- .bitcast,
- .coerce_result_ptr,
- .xor,
- .error_union_type,
- .merge_error_sets,
- .slice_start,
- => BinOp,
-
- .block,
- .block_flat,
- .block_comptime,
- .block_comptime_flat,
- => Block,
-
- .arg => Arg,
- .array_type_sentinel => ArrayTypeSentinel,
- .@"break" => Break,
- .breakvoid => BreakVoid,
- .call => Call,
- .coerce_to_ptr_elem => CoerceToPtrElem,
- .declref => DeclRef,
- .declref_str => DeclRefStr,
- .declval => DeclVal,
- .declval_in_module => DeclValInModule,
- .coerce_result_block_ptr => CoerceResultBlockPtr,
- .compileerror => CompileError,
- .loop => Loop,
- .@"const" => Const,
- .str => Str,
- .int => Int,
- .inttype => IntType,
- .fieldptr => FieldPtr,
- .@"asm" => Asm,
- .@"fn" => Fn,
- .@"export" => Export,
- .param_type => ParamType,
- .primitive => Primitive,
- .fntype => FnType,
- .elemptr => ElemPtr,
- .condbr => CondBr,
- .ptr_type => PtrType,
- .enum_literal => EnumLiteral,
- .error_set => ErrorSet,
- .slice => Slice,
- };
- }
-
- /// Returns whether the instruction is one of the control flow "noreturn" types.
- /// Function calls do not count.
- pub fn isNoReturn(tag: Tag) bool {
- return switch (tag) {
- .add,
- .addwrap,
- .alloc,
- .alloc_inferred,
- .array_cat,
- .array_mul,
- .array_type,
- .array_type_sentinel,
- .arg,
- .as,
- .@"asm",
- .bitand,
- .bitcast,
- .bitcast_ref,
- .bitcast_result_ptr,
- .bitor,
- .block,
- .block_flat,
- .block_comptime,
- .block_comptime_flat,
- .boolnot,
- .breakpoint,
- .call,
- .cmp_lt,
- .cmp_lte,
- .cmp_eq,
- .cmp_gte,
- .cmp_gt,
- .cmp_neq,
- .coerce_result_ptr,
- .coerce_result_block_ptr,
- .coerce_to_ptr_elem,
- .@"const",
- .dbg_stmt,
- .declref,
- .declref_str,
- .declval,
- .declval_in_module,
- .deref,
- .div,
- .elemptr,
- .ensure_result_used,
- .ensure_result_non_error,
- .ensure_indexable,
- .@"export",
- .floatcast,
- .fieldptr,
- .@"fn",
- .fntype,
- .int,
- .intcast,
- .inttype,
- .isnonnull,
- .isnull,
- .iserr,
- .mod_rem,
- .mul,
- .mulwrap,
- .param_type,
- .primitive,
- .ptrtoint,
- .ref,
- .ret_ptr,
- .ret_type,
- .shl,
- .shr,
- .single_const_ptr_type,
- .single_mut_ptr_type,
- .many_const_ptr_type,
- .many_mut_ptr_type,
- .c_const_ptr_type,
- .c_mut_ptr_type,
- .mut_slice_type,
- .const_slice_type,
- .store,
- .str,
- .sub,
- .subwrap,
- .typeof,
- .xor,
- .optional_type,
- .unwrap_optional_safe,
- .unwrap_optional_unsafe,
- .unwrap_err_safe,
- .unwrap_err_unsafe,
- .unwrap_err_code,
- .ptr_type,
- .ensure_err_payload_void,
- .enum_literal,
- .merge_error_sets,
- .anyframe_type,
- .error_union_type,
- .bitnot,
- .error_set,
- .slice,
- .slice_start,
- => false,
-
- .@"break",
- .breakvoid,
- .condbr,
- .compileerror,
- .@"return",
- .returnvoid,
- .unreach_nocheck,
- .@"unreachable",
- .loop,
- => true,
- };
- }
- };
-
- /// Prefer `castTag` to this.
- pub fn cast(base: *Inst, comptime T: type) ?*T {
- if (@hasField(T, "base_tag")) {
- return base.castTag(T.base_tag);
- }
- inline for (@typeInfo(Tag).Enum.fields) |field| {
- const tag = @intToEnum(Tag, field.value);
- if (base.tag == tag) {
- if (T == tag.Type()) {
- return @fieldParentPtr(T, "base", base);
- }
- return null;
- }
- }
- unreachable;
- }
-
- pub fn castTag(base: *Inst, comptime tag: Tag) ?*tag.Type() {
- if (base.tag == tag) {
- return @fieldParentPtr(tag.Type(), "base", base);
- }
- return null;
- }
-
- pub const NoOp = struct {
- base: Inst,
-
- positionals: struct {},
- kw_args: struct {},
- };
-
- pub const UnOp = struct {
- base: Inst,
-
- positionals: struct {
- operand: *Inst,
- },
- kw_args: struct {},
- };
-
- pub const BinOp = struct {
- base: Inst,
-
- positionals: struct {
- lhs: *Inst,
- rhs: *Inst,
- },
- kw_args: struct {},
- };
-
- pub const Arg = struct {
- pub const base_tag = Tag.arg;
- base: Inst,
-
- positionals: struct {
- name: []const u8,
- },
- kw_args: struct {},
- };
-
- pub const Block = struct {
- pub const base_tag = Tag.block;
- base: Inst,
-
- positionals: struct {
- body: Module.Body,
- },
- kw_args: struct {},
- };
-
- pub const Break = struct {
- pub const base_tag = Tag.@"break";
- base: Inst,
-
- positionals: struct {
- block: *Block,
- operand: *Inst,
- },
- kw_args: struct {},
- };
-
- pub const BreakVoid = struct {
- pub const base_tag = Tag.breakvoid;
- base: Inst,
-
- positionals: struct {
- block: *Block,
- },
- kw_args: struct {},
- };
-
- pub const Call = struct {
- pub const base_tag = Tag.call;
- base: Inst,
-
- positionals: struct {
- func: *Inst,
- args: []*Inst,
- },
- kw_args: struct {
- modifier: std.builtin.CallOptions.Modifier = .auto,
- },
- };
-
- pub const CoerceToPtrElem = struct {
- pub const base_tag = Tag.coerce_to_ptr_elem;
- base: Inst,
-
- positionals: struct {
- ptr: *Inst,
- value: *Inst,
- },
- kw_args: struct {},
- };
-
- pub const DeclRef = struct {
- pub const base_tag = Tag.declref;
- base: Inst,
-
- positionals: struct {
- name: []const u8,
- },
- kw_args: struct {},
- };
-
- pub const DeclRefStr = struct {
- pub const base_tag = Tag.declref_str;
- base: Inst,
-
- positionals: struct {
- name: *Inst,
- },
- kw_args: struct {},
- };
-
- pub const DeclVal = struct {
- pub const base_tag = Tag.declval;
- base: Inst,
-
- positionals: struct {
- name: []const u8,
- },
- kw_args: struct {},
- };
-
- pub const DeclValInModule = struct {
- pub const base_tag = Tag.declval_in_module;
- base: Inst,
-
- positionals: struct {
- decl: *IrModule.Decl,
- },
- kw_args: struct {},
- };
-
- pub const CoerceResultBlockPtr = struct {
- pub const base_tag = Tag.coerce_result_block_ptr;
- base: Inst,
-
- positionals: struct {
- dest_type: *Inst,
- block: *Block,
- },
- kw_args: struct {},
- };
-
- pub const CompileError = struct {
- pub const base_tag = Tag.compileerror;
- base: Inst,
-
- positionals: struct {
- msg: []const u8,
- },
- kw_args: struct {},
- };
-
- pub const Const = struct {
- pub const base_tag = Tag.@"const";
- base: Inst,
-
- positionals: struct {
- typed_value: TypedValue,
- },
- kw_args: struct {},
- };
-
- pub const Str = struct {
- pub const base_tag = Tag.str;
- base: Inst,
-
- positionals: struct {
- bytes: []const u8,
- },
- kw_args: struct {},
- };
-
- pub const Int = struct {
- pub const base_tag = Tag.int;
- base: Inst,
-
- positionals: struct {
- int: BigIntConst,
- },
- kw_args: struct {},
- };
-
- pub const Loop = struct {
- pub const base_tag = Tag.loop;
- base: Inst,
-
- positionals: struct {
- body: Module.Body,
- },
- kw_args: struct {},
- };
-
- pub const FieldPtr = struct {
- pub const base_tag = Tag.fieldptr;
- base: Inst,
-
- positionals: struct {
- object_ptr: *Inst,
- field_name: *Inst,
- },
- kw_args: struct {},
- };
-
- pub const Asm = struct {
- pub const base_tag = Tag.@"asm";
- base: Inst,
-
- positionals: struct {
- asm_source: *Inst,
- return_type: *Inst,
- },
- kw_args: struct {
- @"volatile": bool = false,
- output: ?*Inst = null,
- inputs: []*Inst = &[0]*Inst{},
- clobbers: []*Inst = &[0]*Inst{},
- args: []*Inst = &[0]*Inst{},
- },
- };
-
- pub const Fn = struct {
- pub const base_tag = Tag.@"fn";
- base: Inst,
-
- positionals: struct {
- fn_type: *Inst,
- body: Module.Body,
- },
- kw_args: struct {},
- };
-
- pub const FnType = struct {
- pub const base_tag = Tag.fntype;
- base: Inst,
-
- positionals: struct {
- param_types: []*Inst,
- return_type: *Inst,
- },
- kw_args: struct {
- cc: std.builtin.CallingConvention = .Unspecified,
- },
- };
-
- pub const IntType = struct {
- pub const base_tag = Tag.inttype;
- base: Inst,
-
- positionals: struct {
- signed: *Inst,
- bits: *Inst,
- },
- kw_args: struct {},
- };
-
- pub const Export = struct {
- pub const base_tag = Tag.@"export";
- base: Inst,
-
- positionals: struct {
- symbol_name: *Inst,
- decl_name: []const u8,
- },
- kw_args: struct {},
- };
-
- pub const ParamType = struct {
- pub const base_tag = Tag.param_type;
- base: Inst,
-
- positionals: struct {
- func: *Inst,
- arg_index: usize,
- },
- kw_args: struct {},
- };
-
- pub const Primitive = struct {
- pub const base_tag = Tag.primitive;
- base: Inst,
-
- positionals: struct {
- tag: Builtin,
- },
- kw_args: struct {},
-
- pub const Builtin = enum {
- i8,
- u8,
- i16,
- u16,
- i32,
- u32,
- i64,
- u64,
- isize,
- usize,
- c_short,
- c_ushort,
- c_int,
- c_uint,
- c_long,
- c_ulong,
- c_longlong,
- c_ulonglong,
- c_longdouble,
- c_void,
- f16,
- f32,
- f64,
- f128,
- bool,
- void,
- noreturn,
- type,
- anyerror,
- comptime_int,
- comptime_float,
- @"true",
- @"false",
- @"null",
- @"undefined",
- void_value,
-
- pub fn toTypedValue(self: Builtin) TypedValue {
- return switch (self) {
- .i8 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i8_type) },
- .u8 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u8_type) },
- .i16 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i16_type) },
- .u16 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u16_type) },
- .i32 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i32_type) },
- .u32 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u32_type) },
- .i64 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i64_type) },
- .u64 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u64_type) },
- .isize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.isize_type) },
- .usize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.usize_type) },
- .c_short => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_short_type) },
- .c_ushort => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_ushort_type) },
- .c_int => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_int_type) },
- .c_uint => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_uint_type) },
- .c_long => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_long_type) },
- .c_ulong => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_ulong_type) },
- .c_longlong => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_longlong_type) },
- .c_ulonglong => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_ulonglong_type) },
- .c_longdouble => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_longdouble_type) },
- .c_void => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_void_type) },
- .f16 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f16_type) },
- .f32 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f32_type) },
- .f64 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f64_type) },
- .f128 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f128_type) },
- .bool => .{ .ty = Type.initTag(.type), .val = Value.initTag(.bool_type) },
- .void => .{ .ty = Type.initTag(.type), .val = Value.initTag(.void_type) },
- .noreturn => .{ .ty = Type.initTag(.type), .val = Value.initTag(.noreturn_type) },
- .type => .{ .ty = Type.initTag(.type), .val = Value.initTag(.type_type) },
- .anyerror => .{ .ty = Type.initTag(.type), .val = Value.initTag(.anyerror_type) },
- .comptime_int => .{ .ty = Type.initTag(.type), .val = Value.initTag(.comptime_int_type) },
- .comptime_float => .{ .ty = Type.initTag(.type), .val = Value.initTag(.comptime_float_type) },
- .@"true" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_true) },
- .@"false" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_false) },
- .@"null" => .{ .ty = Type.initTag(.@"null"), .val = Value.initTag(.null_value) },
- .@"undefined" => .{ .ty = Type.initTag(.@"undefined"), .val = Value.initTag(.undef) },
- .void_value => .{ .ty = Type.initTag(.void), .val = Value.initTag(.void_value) },
- };
- }
- };
- };
-
- pub const ElemPtr = struct {
- pub const base_tag = Tag.elemptr;
- base: Inst,
-
- positionals: struct {
- array_ptr: *Inst,
- index: *Inst,
- },
- kw_args: struct {},
- };
-
- pub const CondBr = struct {
- pub const base_tag = Tag.condbr;
- base: Inst,
-
- positionals: struct {
- condition: *Inst,
- then_body: Module.Body,
- else_body: Module.Body,
- },
- kw_args: struct {},
- };
-
- pub const PtrType = struct {
- pub const base_tag = Tag.ptr_type;
- base: Inst,
-
- positionals: struct {
- child_type: *Inst,
- },
- kw_args: struct {
- @"allowzero": bool = false,
- @"align": ?*Inst = null,
- align_bit_start: ?*Inst = null,
- align_bit_end: ?*Inst = null,
- mutable: bool = true,
- @"volatile": bool = false,
- sentinel: ?*Inst = null,
- size: std.builtin.TypeInfo.Pointer.Size = .One,
- },
- };
-
- pub const ArrayTypeSentinel = struct {
- pub const base_tag = Tag.array_type_sentinel;
- base: Inst,
-
- positionals: struct {
- len: *Inst,
- sentinel: *Inst,
- elem_type: *Inst,
- },
- kw_args: struct {},
- };
-
- pub const EnumLiteral = struct {
- pub const base_tag = Tag.enum_literal;
- base: Inst,
-
- positionals: struct {
- name: []const u8,
- },
- kw_args: struct {},
- };
-
- pub const ErrorSet = struct {
- pub const base_tag = Tag.error_set;
- base: Inst,
-
- positionals: struct {
- fields: [][]const u8,
- },
- kw_args: struct {},
- };
-
- pub const Slice = struct {
- pub const base_tag = Tag.slice;
- base: Inst,
-
- positionals: struct {
- array_ptr: *Inst,
- start: *Inst,
- },
- kw_args: struct {
- end: ?*Inst = null,
- sentinel: ?*Inst = null,
- },
- };
-};
-
-pub const ErrorMsg = struct {
- byte_offset: usize,
- msg: []const u8,
-};
-
-pub const Module = struct {
- decls: []*Decl,
- arena: std.heap.ArenaAllocator,
- error_msg: ?ErrorMsg = null,
- metadata: std.AutoHashMap(*Inst, MetaData),
- body_metadata: std.AutoHashMap(*Body, BodyMetaData),
-
- pub const MetaData = struct {
- deaths: ir.Inst.DeathsInt,
- addr: usize,
- };
-
- pub const BodyMetaData = struct {
- deaths: []*Inst,
- };
-
- pub const Body = struct {
- instructions: []*Inst,
- };
-
- pub fn deinit(self: *Module, allocator: *Allocator) void {
- self.metadata.deinit();
- self.body_metadata.deinit();
- allocator.free(self.decls);
- self.arena.deinit();
- self.* = undefined;
- }
-
- /// This is a debugging utility for rendering the tree to stderr.
- pub fn dump(self: Module) void {
- self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {};
- }
-
- const DeclAndIndex = struct {
- decl: *Decl,
- index: usize,
- };
-
- /// TODO Look into making a table to speed this up.
- pub fn findDecl(self: Module, name: []const u8) ?DeclAndIndex {
- for (self.decls) |decl, i| {
- if (mem.eql(u8, decl.name, name)) {
- return DeclAndIndex{
- .decl = decl,
- .index = i,
- };
- }
- }
- return null;
- }
-
- pub fn findInstDecl(self: Module, inst: *Inst) ?DeclAndIndex {
- for (self.decls) |decl, i| {
- if (decl.inst == inst) {
- return DeclAndIndex{
- .decl = decl,
- .index = i,
- };
- }
- }
- return null;
- }
-
- /// The allocator is used for temporary storage, but this function always returns
- /// with no resources allocated.
- pub fn writeToStream(self: Module, allocator: *Allocator, stream: anytype) !void {
- var write = Writer{
- .module = &self,
- .inst_table = InstPtrTable.init(allocator),
- .block_table = std.AutoHashMap(*Inst.Block, []const u8).init(allocator),
- .loop_table = std.AutoHashMap(*Inst.Loop, []const u8).init(allocator),
- .arena = std.heap.ArenaAllocator.init(allocator),
- .indent = 2,
- .next_instr_index = undefined,
- };
- defer write.arena.deinit();
- defer write.inst_table.deinit();
- defer write.block_table.deinit();
- defer write.loop_table.deinit();
-
- // First, build a map of *Inst to @ or % indexes
- try write.inst_table.ensureCapacity(@intCast(u32, self.decls.len));
-
- for (self.decls) |decl, decl_i| {
- try write.inst_table.putNoClobber(decl.inst, .{ .inst = decl.inst, .index = null, .name = decl.name });
- }
-
- for (self.decls) |decl, i| {
- write.next_instr_index = 0;
- try stream.print("@{} ", .{decl.name});
- try write.writeInstToStream(stream, decl.inst);
- try stream.writeByte('\n');
- }
- }
-};
-
-const InstPtrTable = std.AutoHashMap(*Inst, struct { inst: *Inst, index: ?usize, name: []const u8 });
-
-const Writer = struct {
- module: *const Module,
- inst_table: InstPtrTable,
- block_table: std.AutoHashMap(*Inst.Block, []const u8),
- loop_table: std.AutoHashMap(*Inst.Loop, []const u8),
- arena: std.heap.ArenaAllocator,
- indent: usize,
- next_instr_index: usize,
-
- fn writeInstToStream(
- self: *Writer,
- stream: anytype,
- inst: *Inst,
- ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
- inline for (@typeInfo(Inst.Tag).Enum.fields) |enum_field| {
- const expected_tag = @field(Inst.Tag, enum_field.name);
- if (inst.tag == expected_tag) {
- return self.writeInstToStreamGeneric(stream, expected_tag, inst);
- }
- }
- unreachable; // all tags handled
- }
-
- fn writeInstToStreamGeneric(
- self: *Writer,
- stream: anytype,
- comptime inst_tag: Inst.Tag,
- base: *Inst,
- ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
- const SpecificInst = inst_tag.Type();
- const inst = @fieldParentPtr(SpecificInst, "base", base);
- const Positionals = @TypeOf(inst.positionals);
- try stream.writeAll("= " ++ @tagName(inst_tag) ++ "(");
- const pos_fields = @typeInfo(Positionals).Struct.fields;
- inline for (pos_fields) |arg_field, i| {
- if (i != 0) {
- try stream.writeAll(", ");
- }
- try self.writeParamToStream(stream, &@field(inst.positionals, arg_field.name));
- }
-
- comptime var need_comma = pos_fields.len != 0;
- const KW_Args = @TypeOf(inst.kw_args);
- inline for (@typeInfo(KW_Args).Struct.fields) |arg_field, i| {
- if (@typeInfo(arg_field.field_type) == .Optional) {
- if (@field(inst.kw_args, arg_field.name)) |non_optional| {
- if (need_comma) try stream.writeAll(", ");
- try stream.print("{}=", .{arg_field.name});
- try self.writeParamToStream(stream, &non_optional);
- need_comma = true;
- }
- } else {
- if (need_comma) try stream.writeAll(", ");
- try stream.print("{}=", .{arg_field.name});
- try self.writeParamToStream(stream, &@field(inst.kw_args, arg_field.name));
- need_comma = true;
- }
- }
-
- try stream.writeByte(')');
- }
-
- fn writeParamToStream(self: *Writer, stream: anytype, param_ptr: anytype) !void {
- const param = param_ptr.*;
- if (@typeInfo(@TypeOf(param)) == .Enum) {
- return stream.writeAll(@tagName(param));
- }
- switch (@TypeOf(param)) {
- *Inst => return self.writeInstParamToStream(stream, param),
- []*Inst => {
- try stream.writeByte('[');
- for (param) |inst, i| {
- if (i != 0) {
- try stream.writeAll(", ");
- }
- try self.writeInstParamToStream(stream, inst);
- }
- try stream.writeByte(']');
- },
- Module.Body => {
- try stream.writeAll("{\n");
- if (self.module.body_metadata.get(param_ptr)) |metadata| {
- if (metadata.deaths.len > 0) {
- try stream.writeByteNTimes(' ', self.indent);
- try stream.writeAll("; deaths={");
- for (metadata.deaths) |death, i| {
- if (i != 0) try stream.writeAll(", ");
- try self.writeInstParamToStream(stream, death);
- }
- try stream.writeAll("}\n");
- }
- }
-
- for (param.instructions) |inst| {
- const my_i = self.next_instr_index;
- self.next_instr_index += 1;
- try self.inst_table.putNoClobber(inst, .{ .inst = inst, .index = my_i, .name = undefined });
- try stream.writeByteNTimes(' ', self.indent);
- try stream.print("%{} ", .{my_i});
- if (inst.cast(Inst.Block)) |block| {
- const name = try std.fmt.allocPrint(&self.arena.allocator, "label_{}", .{my_i});
- try self.block_table.put(block, name);
- } else if (inst.cast(Inst.Loop)) |loop| {
- const name = try std.fmt.allocPrint(&self.arena.allocator, "loop_{}", .{my_i});
- try self.loop_table.put(loop, name);
- }
- self.indent += 2;
- try self.writeInstToStream(stream, inst);
- if (self.module.metadata.get(inst)) |metadata| {
- try stream.print(" ; deaths=0b{b}", .{metadata.deaths});
- // This is conditionally compiled in because addresses mess up the tests due
- // to Address Space Layout Randomization. It's super useful when debugging
- // codegen.zig though.
- if (!std.builtin.is_test) {
- try stream.print(" 0x{x}", .{metadata.addr});
- }
- }
- self.indent -= 2;
- try stream.writeByte('\n');
- }
- try stream.writeByteNTimes(' ', self.indent - 2);
- try stream.writeByte('}');
- },
- bool => return stream.writeByte("01"[@boolToInt(param)]),
- []u8, []const u8 => return std.zig.renderStringLiteral(param, stream),
- BigIntConst, usize => return stream.print("{}", .{param}),
- TypedValue => unreachable, // this is a special case
- *IrModule.Decl => unreachable, // this is a special case
- *Inst.Block => {
- const name = self.block_table.get(param).?;
- return std.zig.renderStringLiteral(name, stream);
- },
- *Inst.Loop => {
- const name = self.loop_table.get(param).?;
- return std.zig.renderStringLiteral(name, stream);
- },
- [][]const u8 => {
- try stream.writeByte('[');
- for (param) |str, i| {
- if (i != 0) {
- try stream.writeAll(", ");
- }
- try std.zig.renderStringLiteral(str, stream);
- }
- try stream.writeByte(']');
- },
- else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
- }
- }
-
- fn writeInstParamToStream(self: *Writer, stream: anytype, inst: *Inst) !void {
- if (self.inst_table.get(inst)) |info| {
- if (info.index) |i| {
- try stream.print("%{}", .{info.index});
- } else {
- try stream.print("@{}", .{info.name});
- }
- } else if (inst.cast(Inst.DeclVal)) |decl_val| {
- try stream.print("@{}", .{decl_val.positionals.name});
- } else if (inst.cast(Inst.DeclValInModule)) |decl_val| {
- try stream.print("@{}", .{decl_val.positionals.decl.name});
- } else {
- // This should be unreachable in theory, but since ZIR is used for debugging the compiler
- // we output some debug text instead.
- try stream.print("?{}?", .{@tagName(inst.tag)});
- }
- }
-};
-
-pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module {
- var global_name_map = std.StringHashMap(*Inst).init(allocator);
- defer global_name_map.deinit();
-
- var parser: Parser = .{
- .allocator = allocator,
- .arena = std.heap.ArenaAllocator.init(allocator),
- .i = 0,
- .source = source,
- .global_name_map = &global_name_map,
- .decls = .{},
- .unnamed_index = 0,
- .block_table = std.StringHashMap(*Inst.Block).init(allocator),
- .loop_table = std.StringHashMap(*Inst.Loop).init(allocator),
- };
- defer parser.block_table.deinit();
- defer parser.loop_table.deinit();
- errdefer parser.arena.deinit();
-
- parser.parseRoot() catch |err| switch (err) {
- error.ParseFailure => {
- assert(parser.error_msg != null);
- },
- else => |e| return e,
- };
-
- return Module{
- .decls = parser.decls.toOwnedSlice(allocator),
- .arena = parser.arena,
- .error_msg = parser.error_msg,
- .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator),
- .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator),
- };
-}
-
-const Parser = struct {
- allocator: *Allocator,
- arena: std.heap.ArenaAllocator,
- i: usize,
- source: [:0]const u8,
- decls: std.ArrayListUnmanaged(*Decl),
- global_name_map: *std.StringHashMap(*Inst),
- error_msg: ?ErrorMsg = null,
- unnamed_index: usize,
- block_table: std.StringHashMap(*Inst.Block),
- loop_table: std.StringHashMap(*Inst.Loop),
-
- const Body = struct {
- instructions: std.ArrayList(*Inst),
- name_map: *std.StringHashMap(*Inst),
- };
-
- fn parseBody(self: *Parser, body_ctx: ?*Body) !Module.Body {
- var name_map = std.StringHashMap(*Inst).init(self.allocator);
- defer name_map.deinit();
-
- var body_context = Body{
- .instructions = std.ArrayList(*Inst).init(self.allocator),
- .name_map = if (body_ctx) |bctx| bctx.name_map else &name_map,
- };
- defer body_context.instructions.deinit();
-
- try requireEatBytes(self, "{");
- skipSpace(self);
-
- while (true) : (self.i += 1) switch (self.source[self.i]) {
- ';' => _ = try skipToAndOver(self, '\n'),
- '%' => {
- self.i += 1;
- const ident = try skipToAndOver(self, ' ');
- skipSpace(self);
- try requireEatBytes(self, "=");
- skipSpace(self);
- const decl = try parseInstruction(self, &body_context, ident);
- const ident_index = body_context.instructions.items.len;
- if (try body_context.name_map.fetchPut(ident, decl.inst)) |_| {
- return self.fail("redefinition of identifier '{}'", .{ident});
- }
- try body_context.instructions.append(decl.inst);
- continue;
- },
- ' ', '\n' => continue,
- '}' => {
- self.i += 1;
- break;
- },
- else => |byte| return self.failByte(byte),
- };
-
- // Move the instructions to the arena
- const instrs = try self.arena.allocator.alloc(*Inst, body_context.instructions.items.len);
- mem.copy(*Inst, instrs, body_context.instructions.items);
- return Module.Body{ .instructions = instrs };
- }
-
- fn parseStringLiteral(self: *Parser) ![]u8 {
- const start = self.i;
- try self.requireEatBytes("\"");
-
- while (true) : (self.i += 1) switch (self.source[self.i]) {
- '"' => {
- self.i += 1;
- const span = self.source[start..self.i];
- var bad_index: usize = undefined;
- const parsed = std.zig.parseStringLiteral(&self.arena.allocator, span, &bad_index) catch |err| switch (err) {
- error.InvalidCharacter => {
- self.i = start + bad_index;
- const bad_byte = self.source[self.i];
- return self.fail("invalid string literal character: '{c}'\n", .{bad_byte});
- },
- else => |e| return e,
- };
- return parsed;
- },
- '\\' => {
- self.i += 1;
- continue;
- },
- 0 => return self.failByte(0),
- else => continue,
- };
- }
-
- fn parseIntegerLiteral(self: *Parser) !BigIntConst {
- const start = self.i;
- if (self.source[self.i] == '-') self.i += 1;
- while (true) : (self.i += 1) switch (self.source[self.i]) {
- '0'...'9' => continue,
- else => break,
- };
- const number_text = self.source[start..self.i];
- const base = 10;
- // TODO reuse the same array list for this
- const limbs_buffer_len = std.math.big.int.calcSetStringLimbsBufferLen(base, number_text.len);
- const limbs_buffer = try self.allocator.alloc(std.math.big.Limb, limbs_buffer_len);
- defer self.allocator.free(limbs_buffer);
- const limb_len = std.math.big.int.calcSetStringLimbCount(base, number_text.len);
- const limbs = try self.arena.allocator.alloc(std.math.big.Limb, limb_len);
- var result = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
- result.setString(base, number_text, limbs_buffer, self.allocator) catch |err| switch (err) {
- error.InvalidCharacter => {
- self.i = start;
- return self.fail("invalid digit in integer literal", .{});
- },
- };
- return result.toConst();
- }
-
- fn parseRoot(self: *Parser) !void {
- // The IR format is designed so that it can be tokenized and parsed at the same time.
- while (true) {
- switch (self.source[self.i]) {
- ';' => _ = try skipToAndOver(self, '\n'),
- '@' => {
- self.i += 1;
- const ident = try skipToAndOver(self, ' ');
- skipSpace(self);
- try requireEatBytes(self, "=");
- skipSpace(self);
- const decl = try parseInstruction(self, null, ident);
- const ident_index = self.decls.items.len;
- if (try self.global_name_map.fetchPut(ident, decl.inst)) |_| {
- return self.fail("redefinition of identifier '{}'", .{ident});
- }
- try self.decls.append(self.allocator, decl);
- },
- ' ', '\n' => self.i += 1,
- 0 => break,
- else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
- }
- }
- }
-
- fn eatByte(self: *Parser, byte: u8) bool {
- if (self.source[self.i] != byte) return false;
- self.i += 1;
- return true;
- }
-
- fn skipSpace(self: *Parser) void {
- while (self.source[self.i] == ' ' or self.source[self.i] == '\n') {
- self.i += 1;
- }
- }
-
- fn requireEatBytes(self: *Parser, bytes: []const u8) !void {
- const start = self.i;
- for (bytes) |byte| {
- if (self.source[self.i] != byte) {
- self.i = start;
- return self.fail("expected '{}'", .{bytes});
- }
- self.i += 1;
- }
- }
-
- fn skipToAndOver(self: *Parser, byte: u8) ![]const u8 {
- const start_i = self.i;
- while (self.source[self.i] != 0) : (self.i += 1) {
- if (self.source[self.i] == byte) {
- const result = self.source[start_i..self.i];
- self.i += 1;
- return result;
- }
- }
- return self.fail("unexpected EOF", .{});
- }
-
- /// ParseFailure is an internal error code; handled in `parse`.
- const InnerError = error{ ParseFailure, OutOfMemory };
-
- fn failByte(self: *Parser, byte: u8) InnerError {
- if (byte == 0) {
- return self.fail("unexpected EOF", .{});
- } else {
- return self.fail("unexpected byte: '{c}'", .{byte});
- }
- }
-
- fn fail(self: *Parser, comptime format: []const u8, args: anytype) InnerError {
- @setCold(true);
- self.error_msg = ErrorMsg{
- .byte_offset = self.i,
- .msg = try std.fmt.allocPrint(&self.arena.allocator, format, args),
- };
- return error.ParseFailure;
- }
-
- fn parseInstruction(self: *Parser, body_ctx: ?*Body, name: []const u8) InnerError!*Decl {
- const contents_start = self.i;
- const fn_name = try skipToAndOver(self, '(');
- inline for (@typeInfo(Inst.Tag).Enum.fields) |field| {
- if (mem.eql(u8, field.name, fn_name)) {
- const tag = @field(Inst.Tag, field.name);
- return parseInstructionGeneric(self, field.name, tag.Type(), tag, body_ctx, name, contents_start);
- }
- }
- return self.fail("unknown instruction '{}'", .{fn_name});
- }
-
- fn parseInstructionGeneric(
- self: *Parser,
- comptime fn_name: []const u8,
- comptime InstType: type,
- tag: Inst.Tag,
- body_ctx: ?*Body,
- inst_name: []const u8,
- contents_start: usize,
- ) InnerError!*Decl {
- const inst_specific = try self.arena.allocator.create(InstType);
- inst_specific.base = .{
- .src = self.i,
- .tag = tag,
- };
-
- if (InstType == Inst.Block) {
- try self.block_table.put(inst_name, inst_specific);
- } else if (InstType == Inst.Loop) {
- try self.loop_table.put(inst_name, inst_specific);
- }
-
- if (@hasField(InstType, "ty")) {
- inst_specific.ty = opt_type orelse {
- return self.fail("instruction '" ++ fn_name ++ "' requires type", .{});
- };
- }
-
- const Positionals = @TypeOf(inst_specific.positionals);
- inline for (@typeInfo(Positionals).Struct.fields) |arg_field| {
- if (self.source[self.i] == ',') {
- self.i += 1;
- skipSpace(self);
- } else if (self.source[self.i] == ')') {
- return self.fail("expected positional parameter '{}'", .{arg_field.name});
- }
- @field(inst_specific.positionals, arg_field.name) = try parseParameterGeneric(
- self,
- arg_field.field_type,
- body_ctx,
- );
- skipSpace(self);
- }
-
- const KW_Args = @TypeOf(inst_specific.kw_args);
- inst_specific.kw_args = .{}; // assign defaults
- skipSpace(self);
- while (eatByte(self, ',')) {
- skipSpace(self);
- const name = try skipToAndOver(self, '=');
- inline for (@typeInfo(KW_Args).Struct.fields) |arg_field| {
- const field_name = arg_field.name;
- if (mem.eql(u8, name, field_name)) {
- const NonOptional = switch (@typeInfo(arg_field.field_type)) {
- .Optional => |info| info.child,
- else => arg_field.field_type,
- };
- @field(inst_specific.kw_args, field_name) = try parseParameterGeneric(self, NonOptional, body_ctx);
- break;
- }
- } else {
- return self.fail("unrecognized keyword parameter: '{}'", .{name});
- }
- skipSpace(self);
- }
- try requireEatBytes(self, ")");
-
- const decl = try self.arena.allocator.create(Decl);
- decl.* = .{
- .name = inst_name,
- .contents_hash = std.zig.hashSrc(self.source[contents_start..self.i]),
- .inst = &inst_specific.base,
- };
- //std.debug.warn("parsed {} = '{}'\n", .{ inst_specific.base.name, inst_specific.base.contents });
-
- return decl;
- }
-
- fn parseParameterGeneric(self: *Parser, comptime T: type, body_ctx: ?*Body) !T {
- if (@typeInfo(T) == .Enum) {
- const start = self.i;
- while (true) : (self.i += 1) switch (self.source[self.i]) {
- ' ', '\n', ',', ')' => {
- const enum_name = self.source[start..self.i];
- return std.meta.stringToEnum(T, enum_name) orelse {
- return self.fail("tag '{}' not a member of enum '{}'", .{ enum_name, @typeName(T) });
- };
- },
- 0 => return self.failByte(0),
- else => continue,
- };
- }
- switch (T) {
- Module.Body => return parseBody(self, body_ctx),
- bool => {
- const bool_value = switch (self.source[self.i]) {
- '0' => false,
- '1' => true,
- else => |byte| return self.fail("expected '0' or '1' for boolean value, found {c}", .{byte}),
- };
- self.i += 1;
- return bool_value;
- },
- []*Inst => {
- try requireEatBytes(self, "[");
- skipSpace(self);
- if (eatByte(self, ']')) return &[0]*Inst{};
-
- var instructions = std.ArrayList(*Inst).init(&self.arena.allocator);
- while (true) {
- skipSpace(self);
- try instructions.append(try parseParameterInst(self, body_ctx));
- skipSpace(self);
- if (!eatByte(self, ',')) break;
- }
- try requireEatBytes(self, "]");
- return instructions.toOwnedSlice();
- },
- *Inst => return parseParameterInst(self, body_ctx),
- []u8, []const u8 => return self.parseStringLiteral(),
- BigIntConst => return self.parseIntegerLiteral(),
- usize => {
- const big_int = try self.parseIntegerLiteral();
- return big_int.to(usize) catch |err| return self.fail("integer literal: {}", .{@errorName(err)});
- },
- TypedValue => return self.fail("'const' is a special instruction; not legal in ZIR text", .{}),
- *IrModule.Decl => return self.fail("'declval_in_module' is a special instruction; not legal in ZIR text", .{}),
- *Inst.Block => {
- const name = try self.parseStringLiteral();
- return self.block_table.get(name).?;
- },
- *Inst.Loop => {
- const name = try self.parseStringLiteral();
- return self.loop_table.get(name).?;
- },
- [][]const u8 => {
- try requireEatBytes(self, "[");
- skipSpace(self);
- if (eatByte(self, ']')) return &[0][]const u8{};
-
- var strings = std.ArrayList([]const u8).init(&self.arena.allocator);
- while (true) {
- skipSpace(self);
- try strings.append(try self.parseStringLiteral());
- skipSpace(self);
- if (!eatByte(self, ',')) break;
- }
- try requireEatBytes(self, "]");
- return strings.toOwnedSlice();
- },
- else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
- }
- return self.fail("TODO parse parameter {}", .{@typeName(T)});
- }
-
- fn parseParameterInst(self: *Parser, body_ctx: ?*Body) !*Inst {
- const local_ref = switch (self.source[self.i]) {
- '@' => false,
- '%' => true,
- else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
- };
- const map = if (local_ref)
- if (body_ctx) |bc|
- bc.name_map
- else
- return self.fail("referencing a % instruction in global scope", .{})
- else
- self.global_name_map;
-
- self.i += 1;
- const name_start = self.i;
- while (true) : (self.i += 1) switch (self.source[self.i]) {
- 0, ' ', '\n', ',', ')', ']' => break,
- else => continue,
- };
- const ident = self.source[name_start..self.i];
- return map.get(ident) orelse {
- const bad_name = self.source[name_start - 1 .. self.i];
- const src = name_start - 1;
- if (local_ref) {
- self.i = src;
- return self.fail("unrecognized identifier: {}", .{bad_name});
- } else {
- const declval = try self.arena.allocator.create(Inst.DeclVal);
- declval.* = .{
- .base = .{
- .src = src,
- .tag = Inst.DeclVal.base_tag,
- },
- .positionals = .{ .name = ident },
- .kw_args = .{},
- };
- return &declval.base;
- }
- };
- }
-
- fn generateName(self: *Parser) ![]u8 {
- const result = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${}", .{self.unnamed_index});
- self.unnamed_index += 1;
- return result;
- }
-};
-
-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,
- .next_auto_name = 0,
- .names = std.StringArrayHashMap(void).init(allocator),
- .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator),
- .indent = 0,
- .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator),
- .loop_table = std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop).init(allocator),
- .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator),
- .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator),
- };
- errdefer ctx.metadata.deinit();
- errdefer ctx.body_metadata.deinit();
- defer ctx.block_table.deinit();
- defer ctx.loop_table.deinit();
- defer ctx.decls.deinit(allocator);
- defer ctx.names.deinit();
- defer ctx.primitive_table.deinit();
- errdefer ctx.arena.deinit();
-
- try ctx.emit();
-
- return Module{
- .decls = ctx.decls.toOwnedSlice(allocator),
- .arena = ctx.arena,
- .metadata = ctx.metadata,
- .body_metadata = ctx.body_metadata,
- };
-}
-
-/// For debugging purposes, prints a function representation to stderr.
-pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void {
- const allocator = old_module.gpa;
- var ctx: EmitZIR = .{
- .allocator = allocator,
- .decls = .{},
- .arena = std.heap.ArenaAllocator.init(allocator),
- .old_module = &old_module,
- .next_auto_name = 0,
- .names = std.StringHashMap(void).init(allocator),
- .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator),
- .indent = 0,
- .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator),
- .loop_table = std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop).init(allocator),
- .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator),
- .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator),
- };
- defer ctx.metadata.deinit();
- defer ctx.body_metadata.deinit();
- defer ctx.block_table.deinit();
- defer ctx.loop_table.deinit();
- defer ctx.decls.deinit(allocator);
- defer ctx.names.deinit();
- defer ctx.primitive_table.deinit();
- defer ctx.arena.deinit();
-
- const fn_ty = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;
- _ = ctx.emitFn(module_fn, 0, fn_ty) catch |err| {
- std.debug.print("unable to dump function: {}\n", .{err});
- return;
- };
- var module = Module{
- .decls = ctx.decls.items,
- .arena = ctx.arena,
- .metadata = ctx.metadata,
- .body_metadata = ctx.body_metadata,
- };
-
- module.dump();
-}
-
-const EmitZIR = struct {
- allocator: *Allocator,
- arena: std.heap.ArenaAllocator,
- old_module: *const IrModule,
- decls: std.ArrayListUnmanaged(*Decl),
- names: std.StringArrayHashMap(void),
- next_auto_name: usize,
- primitive_table: std.AutoHashMap(Inst.Primitive.Builtin, *Decl),
- indent: usize,
- block_table: std.AutoHashMap(*ir.Inst.Block, *Inst.Block),
- loop_table: std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop),
- metadata: std.AutoHashMap(*Inst, Module.MetaData),
- body_metadata: std.AutoHashMap(*Module.Body, Module.BodyMetaData),
-
- fn emit(self: *EmitZIR) !void {
- // Put all the Decls in a list and sort them by name to avoid nondeterminism introduced
- // by the hash table.
- var src_decls = std.ArrayList(*IrModule.Decl).init(self.allocator);
- defer src_decls.deinit();
- try src_decls.ensureCapacity(self.old_module.decl_table.items().len);
- try self.decls.ensureCapacity(self.allocator, self.old_module.decl_table.items().len);
- try self.names.ensureCapacity(self.old_module.decl_table.items().len);
-
- for (self.old_module.decl_table.items()) |entry| {
- const decl = entry.value;
- src_decls.appendAssumeCapacity(decl);
- self.names.putAssumeCapacityNoClobber(mem.spanZ(decl.name), {});
- }
- std.sort.sort(*IrModule.Decl, src_decls.items, {}, (struct {
- fn lessThan(context: void, a: *IrModule.Decl, b: *IrModule.Decl) bool {
- return a.src_index < b.src_index;
- }
- }).lessThan);
-
- // Emit all the decls.
- for (src_decls.items) |ir_decl| {
- switch (ir_decl.analysis) {
- .unreferenced => continue,
-
- .complete => {},
- .codegen_failure => {}, // We still can emit the ZIR.
- .codegen_failure_retryable => {}, // We still can emit the ZIR.
-
- .in_progress => unreachable,
- .outdated => unreachable,
-
- .sema_failure,
- .sema_failure_retryable,
- .dependency_failure,
- => if (self.old_module.failed_decls.get(ir_decl)) |err_msg| {
- const fail_inst = try self.arena.allocator.create(Inst.CompileError);
- fail_inst.* = .{
- .base = .{
- .src = ir_decl.src(),
- .tag = Inst.CompileError.base_tag,
- },
- .positionals = .{
- .msg = try self.arena.allocator.dupe(u8, err_msg.msg),
- },
- .kw_args = .{},
- };
- const decl = try self.arena.allocator.create(Decl);
- decl.* = .{
- .name = mem.spanZ(ir_decl.name),
- .contents_hash = undefined,
- .inst = &fail_inst.base,
- };
- try self.decls.append(self.allocator, decl);
- continue;
- },
- }
- if (self.old_module.export_owners.get(ir_decl)) |exports| {
- for (exports) |module_export| {
- const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name);
- const export_inst = try self.arena.allocator.create(Inst.Export);
- export_inst.* = .{
- .base = .{
- .src = module_export.src,
- .tag = Inst.Export.base_tag,
- },
- .positionals = .{
- .symbol_name = symbol_name.inst,
- .decl_name = mem.spanZ(module_export.exported_decl.name),
- },
- .kw_args = .{},
- };
- _ = try self.emitUnnamedDecl(&export_inst.base);
- }
- } else {
- const new_decl = try self.emitTypedValue(ir_decl.src(), ir_decl.typed_value.most_recent.typed_value);
- new_decl.name = try self.arena.allocator.dupe(u8, mem.spanZ(ir_decl.name));
- }
- }
- }
-
- const ZirBody = struct {
- inst_table: *std.AutoHashMap(*ir.Inst, *Inst),
- instructions: *std.ArrayList(*Inst),
- };
-
- fn resolveInst(self: *EmitZIR, new_body: ZirBody, inst: *ir.Inst) !*Inst {
- if (inst.cast(ir.Inst.Constant)) |const_inst| {
- const new_inst = if (const_inst.val.cast(Value.Payload.Function)) |func_pl| blk: {
- const owner_decl = func_pl.func.owner_decl;
- break :blk try self.emitDeclVal(inst.src, mem.spanZ(owner_decl.name));
- } else if (const_inst.val.cast(Value.Payload.DeclRef)) |declref| blk: {
- const decl_ref = try self.emitDeclRef(inst.src, declref.decl);
- try new_body.instructions.append(decl_ref);
- break :blk decl_ref;
- } else if (const_inst.val.cast(Value.Payload.Variable)) |var_pl| blk: {
- const owner_decl = var_pl.variable.owner_decl;
- break :blk try self.emitDeclVal(inst.src, mem.spanZ(owner_decl.name));
- } else blk: {
- break :blk (try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val })).inst;
- };
- _ = try new_body.inst_table.put(inst, new_inst);
- return new_inst;
- } else {
- return new_body.inst_table.get(inst).?;
- }
- }
-
- fn emitDeclVal(self: *EmitZIR, src: usize, decl_name: []const u8) !*Inst {
- const declval = try self.arena.allocator.create(Inst.DeclVal);
- declval.* = .{
- .base = .{
- .src = src,
- .tag = Inst.DeclVal.base_tag,
- },
- .positionals = .{ .name = try self.arena.allocator.dupe(u8, decl_name) },
- .kw_args = .{},
- };
- return &declval.base;
- }
-
- fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Decl {
- const big_int_space = try self.arena.allocator.create(Value.BigIntSpace);
- const int_inst = try self.arena.allocator.create(Inst.Int);
- int_inst.* = .{
- .base = .{
- .src = src,
- .tag = Inst.Int.base_tag,
- },
- .positionals = .{
- .int = val.toBigInt(big_int_space),
- },
- .kw_args = .{},
- };
- return self.emitUnnamedDecl(&int_inst.base);
- }
-
- fn emitDeclRef(self: *EmitZIR, src: usize, module_decl: *IrModule.Decl) !*Inst {
- const declref_inst = try self.arena.allocator.create(Inst.DeclRef);
- declref_inst.* = .{
- .base = .{
- .src = src,
- .tag = Inst.DeclRef.base_tag,
- },
- .positionals = .{
- .name = mem.spanZ(module_decl.name),
- },
- .kw_args = .{},
- };
- return &declref_inst.base;
- }
-
- fn emitFn(self: *EmitZIR, module_fn: *IrModule.Fn, src: usize, ty: Type) Allocator.Error!*Decl {
- var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator);
- defer inst_table.deinit();
-
- var instructions = std.ArrayList(*Inst).init(self.allocator);
- defer instructions.deinit();
-
- switch (module_fn.analysis) {
- .queued => unreachable,
- .in_progress => unreachable,
- .success => |body| {
- try self.emitBody(body, &inst_table, &instructions);
- },
- .sema_failure => {
- const err_msg = self.old_module.failed_decls.get(module_fn.owner_decl).?;
- const fail_inst = try self.arena.allocator.create(Inst.CompileError);
- fail_inst.* = .{
- .base = .{
- .src = src,
- .tag = Inst.CompileError.base_tag,
- },
- .positionals = .{
- .msg = try self.arena.allocator.dupe(u8, err_msg.msg),
- },
- .kw_args = .{},
- };
- try instructions.append(&fail_inst.base);
- },
- .dependency_failure => {
- const fail_inst = try self.arena.allocator.create(Inst.CompileError);
- fail_inst.* = .{
- .base = .{
- .src = src,
- .tag = Inst.CompileError.base_tag,
- },
- .positionals = .{
- .msg = try self.arena.allocator.dupe(u8, "depends on another failed Decl"),
- },
- .kw_args = .{},
- };
- try instructions.append(&fail_inst.base);
- },
- }
-
- const fn_type = try self.emitType(src, ty);
-
- const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len);
- mem.copy(*Inst, arena_instrs, instructions.items);
-
- const fn_inst = try self.arena.allocator.create(Inst.Fn);
- fn_inst.* = .{
- .base = .{
- .src = src,
- .tag = Inst.Fn.base_tag,
- },
- .positionals = .{
- .fn_type = fn_type.inst,
- .body = .{ .instructions = arena_instrs },
- },
- .kw_args = .{},
- };
- return self.emitUnnamedDecl(&fn_inst.base);
- }
-
- fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Decl {
- const allocator = &self.arena.allocator;
- if (typed_value.val.cast(Value.Payload.DeclRef)) |decl_ref| {
- const decl = decl_ref.decl;
- return try self.emitUnnamedDecl(try self.emitDeclRef(src, decl));
- } else if (typed_value.val.cast(Value.Payload.Variable)) |variable| {
- return self.emitTypedValue(src, .{
- .ty = typed_value.ty,
- .val = variable.variable.init,
- });
- }
- if (typed_value.val.isUndef()) {
- const as_inst = try self.arena.allocator.create(Inst.BinOp);
- as_inst.* = .{
- .base = .{
- .tag = .as,
- .src = src,
- },
- .positionals = .{
- .lhs = (try self.emitType(src, typed_value.ty)).inst,
- .rhs = (try self.emitPrimitive(src, .@"undefined")).inst,
- },
- .kw_args = .{},
- };
- return self.emitUnnamedDecl(&as_inst.base);
- }
- switch (typed_value.ty.zigTypeTag()) {
- .Pointer => {
- const ptr_elem_type = typed_value.ty.elemType();
- switch (ptr_elem_type.zigTypeTag()) {
- .Array => {
- // TODO more checks to make sure this can be emitted as a string literal
- //const array_elem_type = ptr_elem_type.elemType();
- //if (array_elem_type.eql(Type.initTag(.u8)) and
- // ptr_elem_type.hasSentinel(Value.initTag(.zero)))
- //{
- //}
- const bytes = typed_value.val.toAllocatedBytes(allocator) catch |err| switch (err) {
- error.AnalysisFail => unreachable,
- else => |e| return e,
- };
- return self.emitStringLiteral(src, bytes);
- },
- else => |t| std.debug.panic("TODO implement emitTypedValue for pointer to {}", .{@tagName(t)}),
- }
- },
- .ComptimeInt => return self.emitComptimeIntVal(src, typed_value.val),
- .Int => {
- const as_inst = try self.arena.allocator.create(Inst.BinOp);
- as_inst.* = .{
- .base = .{
- .tag = .as,
- .src = src,
- },
- .positionals = .{
- .lhs = (try self.emitType(src, typed_value.ty)).inst,
- .rhs = (try self.emitComptimeIntVal(src, typed_value.val)).inst,
- },
- .kw_args = .{},
- };
- return self.emitUnnamedDecl(&as_inst.base);
- },
- .Type => {
- const ty = try typed_value.val.toType(&self.arena.allocator);
- return self.emitType(src, ty);
- },
- .Fn => {
- const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
- return self.emitFn(module_fn, src, typed_value.ty);
- },
- .Array => {
- // TODO more checks to make sure this can be emitted as a string literal
- //const array_elem_type = ptr_elem_type.elemType();
- //if (array_elem_type.eql(Type.initTag(.u8)) and
- // ptr_elem_type.hasSentinel(Value.initTag(.zero)))
- //{
- //}
- const bytes = typed_value.val.toAllocatedBytes(allocator) catch |err| switch (err) {
- error.AnalysisFail => unreachable,
- else => |e| return e,
- };
- const str_inst = try self.arena.allocator.create(Inst.Str);
- str_inst.* = .{
- .base = .{
- .src = src,
- .tag = Inst.Str.base_tag,
- },
- .positionals = .{
- .bytes = bytes,
- },
- .kw_args = .{},
- };
- return self.emitUnnamedDecl(&str_inst.base);
- },
- .Void => return self.emitPrimitive(src, .void_value),
- .Bool => if (typed_value.val.toBool())
- return self.emitPrimitive(src, .@"true")
- else
- return self.emitPrimitive(src, .@"false"),
- .EnumLiteral => {
- const enum_literal = @fieldParentPtr(Value.Payload.Bytes, "base", typed_value.val.ptr_otherwise);
- const inst = try self.arena.allocator.create(Inst.Str);
- inst.* = .{
- .base = .{
- .src = src,
- .tag = .enum_literal,
- },
- .positionals = .{
- .bytes = enum_literal.data,
- },
- .kw_args = .{},
- };
- return self.emitUnnamedDecl(&inst.base);
- },
- else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}),
- }
- }
-
- fn emitNoOp(self: *EmitZIR, src: usize, old_inst: *ir.Inst.NoOp, tag: Inst.Tag) Allocator.Error!*Inst {
- const new_inst = try self.arena.allocator.create(Inst.NoOp);
- new_inst.* = .{
- .base = .{
- .src = src,
- .tag = tag,
- },
- .positionals = .{},
- .kw_args = .{},
- };
- return &new_inst.base;
- }
-
- fn emitUnOp(
- self: *EmitZIR,
- src: usize,
- new_body: ZirBody,
- old_inst: *ir.Inst.UnOp,
- tag: Inst.Tag,
- ) Allocator.Error!*Inst {
- const new_inst = try self.arena.allocator.create(Inst.UnOp);
- new_inst.* = .{
- .base = .{
- .src = src,
- .tag = tag,
- },
- .positionals = .{
- .operand = try self.resolveInst(new_body, old_inst.operand),
- },
- .kw_args = .{},
- };
- return &new_inst.base;
- }
-
- fn emitBinOp(
- self: *EmitZIR,
- src: usize,
- new_body: ZirBody,
- old_inst: *ir.Inst.BinOp,
- tag: Inst.Tag,
- ) Allocator.Error!*Inst {
- const new_inst = try self.arena.allocator.create(Inst.BinOp);
- new_inst.* = .{
- .base = .{
- .src = src,
- .tag = tag,
- },
- .positionals = .{
- .lhs = try self.resolveInst(new_body, old_inst.lhs),
- .rhs = try self.resolveInst(new_body, old_inst.rhs),
- },
- .kw_args = .{},
- };
- return &new_inst.base;
- }
-
- fn emitCast(
- self: *EmitZIR,
- src: usize,
- new_body: ZirBody,
- old_inst: *ir.Inst.UnOp,
- tag: Inst.Tag,
- ) Allocator.Error!*Inst {
- const new_inst = try self.arena.allocator.create(Inst.BinOp);
- new_inst.* = .{
- .base = .{
- .src = src,
- .tag = tag,
- },
- .positionals = .{
- .lhs = (try self.emitType(old_inst.base.src, old_inst.base.ty)).inst,
- .rhs = try self.resolveInst(new_body, old_inst.operand),
- },
- .kw_args = .{},
- };
- return &new_inst.base;
- }
-
- fn emitBody(
- self: *EmitZIR,
- body: ir.Body,
- inst_table: *std.AutoHashMap(*ir.Inst, *Inst),
- instructions: *std.ArrayList(*Inst),
- ) Allocator.Error!void {
- const new_body = ZirBody{
- .inst_table = inst_table,
- .instructions = instructions,
- };
- for (body.instructions) |inst| {
- const new_inst = switch (inst.tag) {
- .constant => unreachable, // excluded from function bodies
-
- .breakpoint => try self.emitNoOp(inst.src, inst.castTag(.breakpoint).?, .breakpoint),
- .unreach => try self.emitNoOp(inst.src, inst.castTag(.unreach).?, .unreach_nocheck),
- .retvoid => try self.emitNoOp(inst.src, inst.castTag(.retvoid).?, .returnvoid),
- .dbg_stmt => try self.emitNoOp(inst.src, inst.castTag(.dbg_stmt).?, .dbg_stmt),
-
- .not => try self.emitUnOp(inst.src, new_body, inst.castTag(.not).?, .boolnot),
- .ret => try self.emitUnOp(inst.src, new_body, inst.castTag(.ret).?, .@"return"),
- .ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, .ptrtoint),
- .isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, .isnull),
- .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull),
- .iserr => try self.emitUnOp(inst.src, new_body, inst.castTag(.iserr).?, .iserr),
- .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref),
- .ref => try self.emitUnOp(inst.src, new_body, inst.castTag(.ref).?, .ref),
- .unwrap_optional => try self.emitUnOp(inst.src, new_body, inst.castTag(.unwrap_optional).?, .unwrap_optional_unsafe),
- .wrap_optional => try self.emitCast(inst.src, new_body, inst.castTag(.wrap_optional).?, .as),
-
- .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add),
- .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub),
- .store => try self.emitBinOp(inst.src, new_body, inst.castTag(.store).?, .store),
- .cmp_lt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lt).?, .cmp_lt),
- .cmp_lte => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lte).?, .cmp_lte),
- .cmp_eq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_eq).?, .cmp_eq),
- .cmp_gte => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gte).?, .cmp_gte),
- .cmp_gt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gt).?, .cmp_gt),
- .cmp_neq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_neq).?, .cmp_neq),
-
- .bitcast => try self.emitCast(inst.src, new_body, inst.castTag(.bitcast).?, .bitcast),
- .intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, .intcast),
- .floatcast => try self.emitCast(inst.src, new_body, inst.castTag(.floatcast).?, .floatcast),
-
- .alloc => blk: {
- const new_inst = try self.arena.allocator.create(Inst.UnOp);
- new_inst.* = .{
- .base = .{
- .src = inst.src,
- .tag = .alloc,
- },
- .positionals = .{
- .operand = (try self.emitType(inst.src, inst.ty)).inst,
- },
- .kw_args = .{},
- };
- break :blk &new_inst.base;
- },
-
- .arg => blk: {
- const old_inst = inst.castTag(.arg).?;
- const new_inst = try self.arena.allocator.create(Inst.Arg);
- new_inst.* = .{
- .base = .{
- .src = inst.src,
- .tag = .arg,
- },
- .positionals = .{
- .name = try self.arena.allocator.dupe(u8, mem.spanZ(old_inst.name)),
- },
- .kw_args = .{},
- };
- break :blk &new_inst.base;
- },
-
- .block => blk: {
- const old_inst = inst.castTag(.block).?;
- const new_inst = try self.arena.allocator.create(Inst.Block);
-
- try self.block_table.put(old_inst, new_inst);
-
- var block_body = std.ArrayList(*Inst).init(self.allocator);
- defer block_body.deinit();
-
- try self.emitBody(old_inst.body, inst_table, &block_body);
-
- new_inst.* = .{
- .base = .{
- .src = inst.src,
- .tag = Inst.Block.base_tag,
- },
- .positionals = .{
- .body = .{ .instructions = block_body.toOwnedSlice() },
- },
- .kw_args = .{},
- };
-
- break :blk &new_inst.base;
- },
-
- .loop => blk: {
- const old_inst = inst.castTag(.loop).?;
- const new_inst = try self.arena.allocator.create(Inst.Loop);
-
- try self.loop_table.put(old_inst, new_inst);
-
- var loop_body = std.ArrayList(*Inst).init(self.allocator);
- defer loop_body.deinit();
-
- try self.emitBody(old_inst.body, inst_table, &loop_body);
-
- new_inst.* = .{
- .base = .{
- .src = inst.src,
- .tag = Inst.Loop.base_tag,
- },
- .positionals = .{
- .body = .{ .instructions = loop_body.toOwnedSlice() },
- },
- .kw_args = .{},
- };
-
- break :blk &new_inst.base;
- },
-
- .brvoid => blk: {
- const old_inst = inst.cast(ir.Inst.BrVoid).?;
- const new_block = self.block_table.get(old_inst.block).?;
- const new_inst = try self.arena.allocator.create(Inst.BreakVoid);
- new_inst.* = .{
- .base = .{
- .src = inst.src,
- .tag = Inst.BreakVoid.base_tag,
- },
- .positionals = .{
- .block = new_block,
- },
- .kw_args = .{},
- };
- break :blk &new_inst.base;
- },
-
- .br => blk: {
- const old_inst = inst.castTag(.br).?;
- const new_block = self.block_table.get(old_inst.block).?;
- const new_inst = try self.arena.allocator.create(Inst.Break);
- new_inst.* = .{
- .base = .{
- .src = inst.src,
- .tag = Inst.Break.base_tag,
- },
- .positionals = .{
- .block = new_block,
- .operand = try self.resolveInst(new_body, old_inst.operand),
- },
- .kw_args = .{},
- };
- break :blk &new_inst.base;
- },
-
- .call => blk: {
- const old_inst = inst.castTag(.call).?;
- const new_inst = try self.arena.allocator.create(Inst.Call);
-
- const args = try self.arena.allocator.alloc(*Inst, old_inst.args.len);
- for (args) |*elem, i| {
- elem.* = try self.resolveInst(new_body, old_inst.args[i]);
- }
- new_inst.* = .{
- .base = .{
- .src = inst.src,
- .tag = Inst.Call.base_tag,
- },
- .positionals = .{
- .func = try self.resolveInst(new_body, old_inst.func),
- .args = args,
- },
- .kw_args = .{},
- };
- break :blk &new_inst.base;
- },
-
- .assembly => blk: {
- const old_inst = inst.castTag(.assembly).?;
- const new_inst = try self.arena.allocator.create(Inst.Asm);
-
- const inputs = try self.arena.allocator.alloc(*Inst, old_inst.inputs.len);
- for (inputs) |*elem, i| {
- elem.* = (try self.emitStringLiteral(inst.src, old_inst.inputs[i])).inst;
- }
-
- const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.clobbers.len);
- for (clobbers) |*elem, i| {
- elem.* = (try self.emitStringLiteral(inst.src, old_inst.clobbers[i])).inst;
- }
-
- const args = try self.arena.allocator.alloc(*Inst, old_inst.args.len);
- for (args) |*elem, i| {
- elem.* = try self.resolveInst(new_body, old_inst.args[i]);
- }
-
- new_inst.* = .{
- .base = .{
- .src = inst.src,
- .tag = Inst.Asm.base_tag,
- },
- .positionals = .{
- .asm_source = (try self.emitStringLiteral(inst.src, old_inst.asm_source)).inst,
- .return_type = (try self.emitType(inst.src, inst.ty)).inst,
- },
- .kw_args = .{
- .@"volatile" = old_inst.is_volatile,
- .output = if (old_inst.output) |o|
- (try self.emitStringLiteral(inst.src, o)).inst
- else
- null,
- .inputs = inputs,
- .clobbers = clobbers,
- .args = args,
- },
- };
- break :blk &new_inst.base;
- },
-
- .condbr => blk: {
- const old_inst = inst.castTag(.condbr).?;
-
- var then_body = std.ArrayList(*Inst).init(self.allocator);
- var else_body = std.ArrayList(*Inst).init(self.allocator);
-
- defer then_body.deinit();
- defer else_body.deinit();
-
- const then_deaths = try self.arena.allocator.alloc(*Inst, old_inst.thenDeaths().len);
- const else_deaths = try self.arena.allocator.alloc(*Inst, old_inst.elseDeaths().len);
-
- for (old_inst.thenDeaths()) |death, i| {
- then_deaths[i] = try self.resolveInst(new_body, death);
- }
- for (old_inst.elseDeaths()) |death, i| {
- else_deaths[i] = try self.resolveInst(new_body, death);
- }
-
- try self.emitBody(old_inst.then_body, inst_table, &then_body);
- try self.emitBody(old_inst.else_body, inst_table, &else_body);
-
- const new_inst = try self.arena.allocator.create(Inst.CondBr);
-
- try self.body_metadata.put(&new_inst.positionals.then_body, .{ .deaths = then_deaths });
- try self.body_metadata.put(&new_inst.positionals.else_body, .{ .deaths = else_deaths });
-
- new_inst.* = .{
- .base = .{
- .src = inst.src,
- .tag = Inst.CondBr.base_tag,
- },
- .positionals = .{
- .condition = try self.resolveInst(new_body, old_inst.condition),
- .then_body = .{ .instructions = then_body.toOwnedSlice() },
- .else_body = .{ .instructions = else_body.toOwnedSlice() },
- },
- .kw_args = .{},
- };
- break :blk &new_inst.base;
- },
-
- .varptr => @panic("TODO"),
- };
- try self.metadata.put(new_inst, .{
- .deaths = inst.deaths,
- .addr = @ptrToInt(inst),
- });
- try instructions.append(new_inst);
- try inst_table.put(inst, new_inst);
- }
- }
-
- fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Decl {
- switch (ty.tag()) {
- .i8 => return self.emitPrimitive(src, .i8),
- .u8 => return self.emitPrimitive(src, .u8),
- .i16 => return self.emitPrimitive(src, .i16),
- .u16 => return self.emitPrimitive(src, .u16),
- .i32 => return self.emitPrimitive(src, .i32),
- .u32 => return self.emitPrimitive(src, .u32),
- .i64 => return self.emitPrimitive(src, .i64),
- .u64 => return self.emitPrimitive(src, .u64),
- .isize => return self.emitPrimitive(src, .isize),
- .usize => return self.emitPrimitive(src, .usize),
- .c_short => return self.emitPrimitive(src, .c_short),
- .c_ushort => return self.emitPrimitive(src, .c_ushort),
- .c_int => return self.emitPrimitive(src, .c_int),
- .c_uint => return self.emitPrimitive(src, .c_uint),
- .c_long => return self.emitPrimitive(src, .c_long),
- .c_ulong => return self.emitPrimitive(src, .c_ulong),
- .c_longlong => return self.emitPrimitive(src, .c_longlong),
- .c_ulonglong => return self.emitPrimitive(src, .c_ulonglong),
- .c_longdouble => return self.emitPrimitive(src, .c_longdouble),
- .c_void => return self.emitPrimitive(src, .c_void),
- .f16 => return self.emitPrimitive(src, .f16),
- .f32 => return self.emitPrimitive(src, .f32),
- .f64 => return self.emitPrimitive(src, .f64),
- .f128 => return self.emitPrimitive(src, .f128),
- .anyerror => return self.emitPrimitive(src, .anyerror),
- else => switch (ty.zigTypeTag()) {
- .Bool => return self.emitPrimitive(src, .bool),
- .Void => return self.emitPrimitive(src, .void),
- .NoReturn => return self.emitPrimitive(src, .noreturn),
- .Type => return self.emitPrimitive(src, .type),
- .ComptimeInt => return self.emitPrimitive(src, .comptime_int),
- .ComptimeFloat => return self.emitPrimitive(src, .comptime_float),
- .Fn => {
- const param_types = try self.allocator.alloc(Type, ty.fnParamLen());
- defer self.allocator.free(param_types);
-
- ty.fnParamTypes(param_types);
- const emitted_params = try self.arena.allocator.alloc(*Inst, param_types.len);
- for (param_types) |param_type, i| {
- emitted_params[i] = (try self.emitType(src, param_type)).inst;
- }
-
- const fntype_inst = try self.arena.allocator.create(Inst.FnType);
- fntype_inst.* = .{
- .base = .{
- .src = src,
- .tag = Inst.FnType.base_tag,
- },
- .positionals = .{
- .param_types = emitted_params,
- .return_type = (try self.emitType(src, ty.fnReturnType())).inst,
- },
- .kw_args = .{
- .cc = ty.fnCallingConvention(),
- },
- };
- return self.emitUnnamedDecl(&fntype_inst.base);
- },
- .Int => {
- const info = ty.intInfo(self.old_module.target());
- 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 };
- const bits = try self.emitComptimeIntVal(src, Value.initPayload(&bits_payload.base));
- const inttype_inst = try self.arena.allocator.create(Inst.IntType);
- inttype_inst.* = .{
- .base = .{
- .src = src,
- .tag = Inst.IntType.base_tag,
- },
- .positionals = .{
- .signed = signed.inst,
- .bits = bits.inst,
- },
- .kw_args = .{},
- };
- return self.emitUnnamedDecl(&inttype_inst.base);
- },
- .Pointer => {
- if (ty.isSinglePointer()) {
- const inst = try self.arena.allocator.create(Inst.UnOp);
- const tag: Inst.Tag = if (ty.isConstPtr()) .single_const_ptr_type else .single_mut_ptr_type;
- inst.* = .{
- .base = .{
- .src = src,
- .tag = tag,
- },
- .positionals = .{
- .operand = (try self.emitType(src, ty.elemType())).inst,
- },
- .kw_args = .{},
- };
- return self.emitUnnamedDecl(&inst.base);
- } else {
- std.debug.panic("TODO implement emitType for {}", .{ty});
- }
- },
- .Optional => {
- var buf: Type.Payload.PointerSimple = undefined;
- const inst = try self.arena.allocator.create(Inst.UnOp);
- inst.* = .{
- .base = .{
- .src = src,
- .tag = .optional_type,
- },
- .positionals = .{
- .operand = (try self.emitType(src, ty.optionalChild(&buf))).inst,
- },
- .kw_args = .{},
- };
- return self.emitUnnamedDecl(&inst.base);
- },
- .Array => {
- var len_pl = Value.Payload.Int_u64{ .int = ty.arrayLen() };
- const len = Value.initPayload(&len_pl.base);
-
- const inst = if (ty.sentinel()) |sentinel| blk: {
- const inst = try self.arena.allocator.create(Inst.ArrayTypeSentinel);
- inst.* = .{
- .base = .{
- .src = src,
- .tag = .array_type,
- },
- .positionals = .{
- .len = (try self.emitTypedValue(src, .{
- .ty = Type.initTag(.usize),
- .val = len,
- })).inst,
- .sentinel = (try self.emitTypedValue(src, .{
- .ty = ty.elemType(),
- .val = sentinel,
- })).inst,
- .elem_type = (try self.emitType(src, ty.elemType())).inst,
- },
- .kw_args = .{},
- };
- break :blk &inst.base;
- } else blk: {
- const inst = try self.arena.allocator.create(Inst.BinOp);
- inst.* = .{
- .base = .{
- .src = src,
- .tag = .array_type,
- },
- .positionals = .{
- .lhs = (try self.emitTypedValue(src, .{
- .ty = Type.initTag(.usize),
- .val = len,
- })).inst,
- .rhs = (try self.emitType(src, ty.elemType())).inst,
- },
- .kw_args = .{},
- };
- break :blk &inst.base;
- };
- return self.emitUnnamedDecl(inst);
- },
- else => std.debug.panic("TODO implement emitType for {}", .{ty}),
- },
- }
- }
-
- fn autoName(self: *EmitZIR) ![]u8 {
- while (true) {
- const proposed_name = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${}", .{self.next_auto_name});
- self.next_auto_name += 1;
- const gop = try self.names.getOrPut(proposed_name);
- if (!gop.found_existing) {
- gop.entry.value = {};
- return proposed_name;
- }
- }
- }
-
- fn emitPrimitive(self: *EmitZIR, src: usize, tag: Inst.Primitive.Builtin) !*Decl {
- const gop = try self.primitive_table.getOrPut(tag);
- if (!gop.found_existing) {
- const primitive_inst = try self.arena.allocator.create(Inst.Primitive);
- primitive_inst.* = .{
- .base = .{
- .src = src,
- .tag = Inst.Primitive.base_tag,
- },
- .positionals = .{
- .tag = tag,
- },
- .kw_args = .{},
- };
- gop.entry.value = try self.emitUnnamedDecl(&primitive_inst.base);
- }
- return gop.entry.value;
- }
-
- fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Decl {
- const str_inst = try self.arena.allocator.create(Inst.Str);
- str_inst.* = .{
- .base = .{
- .src = src,
- .tag = Inst.Str.base_tag,
- },
- .positionals = .{
- .bytes = str,
- },
- .kw_args = .{},
- };
- return self.emitUnnamedDecl(&str_inst.base);
- }
-
- fn emitUnnamedDecl(self: *EmitZIR, inst: *Inst) !*Decl {
- const decl = try self.arena.allocator.create(Decl);
- decl.* = .{
- .name = try self.autoName(),
- .contents_hash = undefined,
- .inst = inst,
- };
- try self.decls.append(self.allocator, decl);
- return decl;
- }
-};
diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig
deleted file mode 100644
index c99da39c046ce840632de8074d3964daf3aa617e..0000000000000000000000000000000000000000
--- a/src-self-hosted/zir_sema.zig
+++ /dev/null
@@ -1,1595 +0,0 @@
-//! Semantic analysis of ZIR instructions.
-//! This file operates on a `Module` instance, transforming untyped ZIR
-//! instructions into semantically-analyzed IR instructions. It does type
-//! checking, comptime control flow, and safety-check generation. This is the
-//! the heart of the Zig compiler.
-//! When deciding if something goes into this file or into Module, here is a
-//! guiding principle: if it has to do with (untyped) ZIR instructions, it goes
-//! here. If the analysis operates on typed IR instructions, it goes in Module.
-
-const std = @import("std");
-const mem = std.mem;
-const Allocator = std.mem.Allocator;
-const Value = @import("value.zig").Value;
-const Type = @import("type.zig").Type;
-const TypedValue = @import("TypedValue.zig");
-const assert = std.debug.assert;
-const ir = @import("ir.zig");
-const zir = @import("zir.zig");
-const Module = @import("Module.zig");
-const Inst = ir.Inst;
-const Body = ir.Body;
-const trace = @import("tracy.zig").trace;
-const Scope = Module.Scope;
-const InnerError = Module.InnerError;
-const Decl = Module.Decl;
-
-pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
- switch (old_inst.tag) {
- .alloc => return analyzeInstAlloc(mod, scope, old_inst.castTag(.alloc).?),
- .alloc_inferred => return analyzeInstAllocInferred(mod, scope, old_inst.castTag(.alloc_inferred).?),
- .arg => return analyzeInstArg(mod, scope, old_inst.castTag(.arg).?),
- .bitcast_ref => return analyzeInstBitCastRef(mod, scope, old_inst.castTag(.bitcast_ref).?),
- .bitcast_result_ptr => return analyzeInstBitCastResultPtr(mod, scope, old_inst.castTag(.bitcast_result_ptr).?),
- .block => return analyzeInstBlock(mod, scope, old_inst.castTag(.block).?, false),
- .block_comptime => return analyzeInstBlock(mod, scope, old_inst.castTag(.block_comptime).?, true),
- .block_flat => return analyzeInstBlockFlat(mod, scope, old_inst.castTag(.block_flat).?, false),
- .block_comptime_flat => return analyzeInstBlockFlat(mod, scope, old_inst.castTag(.block_comptime_flat).?, true),
- .@"break" => return analyzeInstBreak(mod, scope, old_inst.castTag(.@"break").?),
- .breakpoint => return analyzeInstBreakpoint(mod, scope, old_inst.castTag(.breakpoint).?),
- .breakvoid => return analyzeInstBreakVoid(mod, scope, old_inst.castTag(.breakvoid).?),
- .call => return analyzeInstCall(mod, scope, old_inst.castTag(.call).?),
- .coerce_result_block_ptr => return analyzeInstCoerceResultBlockPtr(mod, scope, old_inst.castTag(.coerce_result_block_ptr).?),
- .coerce_result_ptr => return analyzeInstCoerceResultPtr(mod, scope, old_inst.castTag(.coerce_result_ptr).?),
- .coerce_to_ptr_elem => return analyzeInstCoerceToPtrElem(mod, scope, old_inst.castTag(.coerce_to_ptr_elem).?),
- .compileerror => return analyzeInstCompileError(mod, scope, old_inst.castTag(.compileerror).?),
- .@"const" => return analyzeInstConst(mod, scope, old_inst.castTag(.@"const").?),
- .dbg_stmt => return analyzeInstDbgStmt(mod, scope, old_inst.castTag(.dbg_stmt).?),
- .declref => return analyzeInstDeclRef(mod, scope, old_inst.castTag(.declref).?),
- .declref_str => return analyzeInstDeclRefStr(mod, scope, old_inst.castTag(.declref_str).?),
- .declval => return analyzeInstDeclVal(mod, scope, old_inst.castTag(.declval).?),
- .declval_in_module => return analyzeInstDeclValInModule(mod, scope, old_inst.castTag(.declval_in_module).?),
- .ensure_result_used => return analyzeInstEnsureResultUsed(mod, scope, old_inst.castTag(.ensure_result_used).?),
- .ensure_result_non_error => return analyzeInstEnsureResultNonError(mod, scope, old_inst.castTag(.ensure_result_non_error).?),
- .ensure_indexable => return analyzeInstEnsureIndexable(mod, scope, old_inst.castTag(.ensure_indexable).?),
- .ref => return analyzeInstRef(mod, scope, old_inst.castTag(.ref).?),
- .ret_ptr => return analyzeInstRetPtr(mod, scope, old_inst.castTag(.ret_ptr).?),
- .ret_type => return analyzeInstRetType(mod, scope, old_inst.castTag(.ret_type).?),
- .single_const_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.single_const_ptr_type).?, false, .One),
- .single_mut_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.single_mut_ptr_type).?, true, .One),
- .many_const_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.many_const_ptr_type).?, false, .Many),
- .many_mut_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.many_mut_ptr_type).?, true, .Many),
- .c_const_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.c_const_ptr_type).?, false, .C),
- .c_mut_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.c_mut_ptr_type).?, true, .C),
- .const_slice_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.const_slice_type).?, false, .Slice),
- .mut_slice_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.mut_slice_type).?, true, .Slice),
- .ptr_type => return analyzeInstPtrType(mod, scope, old_inst.castTag(.ptr_type).?),
- .store => return analyzeInstStore(mod, scope, old_inst.castTag(.store).?),
- .str => return analyzeInstStr(mod, scope, old_inst.castTag(.str).?),
- .int => {
- const big_int = old_inst.castTag(.int).?.positionals.int;
- return mod.constIntBig(scope, old_inst.src, Type.initTag(.comptime_int), big_int);
- },
- .inttype => return analyzeInstIntType(mod, scope, old_inst.castTag(.inttype).?),
- .loop => return analyzeInstLoop(mod, scope, old_inst.castTag(.loop).?),
- .param_type => return analyzeInstParamType(mod, scope, old_inst.castTag(.param_type).?),
- .ptrtoint => return analyzeInstPtrToInt(mod, scope, old_inst.castTag(.ptrtoint).?),
- .fieldptr => return analyzeInstFieldPtr(mod, scope, old_inst.castTag(.fieldptr).?),
- .deref => return analyzeInstDeref(mod, scope, old_inst.castTag(.deref).?),
- .as => return analyzeInstAs(mod, scope, old_inst.castTag(.as).?),
- .@"asm" => return analyzeInstAsm(mod, scope, old_inst.castTag(.@"asm").?),
- .@"unreachable" => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.@"unreachable").?, true),
- .unreach_nocheck => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.unreach_nocheck).?, false),
- .@"return" => return analyzeInstRet(mod, scope, old_inst.castTag(.@"return").?),
- .returnvoid => return analyzeInstRetVoid(mod, scope, old_inst.castTag(.returnvoid).?),
- .@"fn" => return analyzeInstFn(mod, scope, old_inst.castTag(.@"fn").?),
- .@"export" => return analyzeInstExport(mod, scope, old_inst.castTag(.@"export").?),
- .primitive => return analyzeInstPrimitive(mod, scope, old_inst.castTag(.primitive).?),
- .fntype => return analyzeInstFnType(mod, scope, old_inst.castTag(.fntype).?),
- .intcast => return analyzeInstIntCast(mod, scope, old_inst.castTag(.intcast).?),
- .bitcast => return analyzeInstBitCast(mod, scope, old_inst.castTag(.bitcast).?),
- .floatcast => return analyzeInstFloatCast(mod, scope, old_inst.castTag(.floatcast).?),
- .elemptr => return analyzeInstElemPtr(mod, scope, old_inst.castTag(.elemptr).?),
- .add => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.add).?),
- .addwrap => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.addwrap).?),
- .sub => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.sub).?),
- .subwrap => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.subwrap).?),
- .mul => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.mul).?),
- .mulwrap => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.mulwrap).?),
- .div => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.div).?),
- .mod_rem => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.mod_rem).?),
- .array_cat => return analyzeInstArrayCat(mod, scope, old_inst.castTag(.array_cat).?),
- .array_mul => return analyzeInstArrayMul(mod, scope, old_inst.castTag(.array_mul).?),
- .bitand => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitand).?),
- .bitnot => return analyzeInstBitNot(mod, scope, old_inst.castTag(.bitnot).?),
- .bitor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitor).?),
- .xor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.xor).?),
- .shl => return analyzeInstShl(mod, scope, old_inst.castTag(.shl).?),
- .shr => return analyzeInstShr(mod, scope, old_inst.castTag(.shr).?),
- .cmp_lt => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_lt).?, .lt),
- .cmp_lte => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_lte).?, .lte),
- .cmp_eq => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_eq).?, .eq),
- .cmp_gte => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_gte).?, .gte),
- .cmp_gt => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_gt).?, .gt),
- .cmp_neq => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_neq).?, .neq),
- .condbr => return analyzeInstCondBr(mod, scope, old_inst.castTag(.condbr).?),
- .isnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnull).?, true),
- .isnonnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnonnull).?, false),
- .iserr => return analyzeInstIsErr(mod, scope, old_inst.castTag(.iserr).?),
- .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?),
- .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?),
- .optional_type => return analyzeInstOptionalType(mod, scope, old_inst.castTag(.optional_type).?),
- .unwrap_optional_safe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_safe).?, true),
- .unwrap_optional_unsafe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_unsafe).?, false),
- .unwrap_err_safe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_safe).?, true),
- .unwrap_err_unsafe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_unsafe).?, false),
- .unwrap_err_code => return analyzeInstUnwrapErrCode(mod, scope, old_inst.castTag(.unwrap_err_code).?),
- .ensure_err_payload_void => return analyzeInstEnsureErrPayloadVoid(mod, scope, old_inst.castTag(.ensure_err_payload_void).?),
- .array_type => return analyzeInstArrayType(mod, scope, old_inst.castTag(.array_type).?),
- .array_type_sentinel => return analyzeInstArrayTypeSentinel(mod, scope, old_inst.castTag(.array_type_sentinel).?),
- .enum_literal => return analyzeInstEnumLiteral(mod, scope, old_inst.castTag(.enum_literal).?),
- .merge_error_sets => return analyzeInstMergeErrorSets(mod, scope, old_inst.castTag(.merge_error_sets).?),
- .error_union_type => return analyzeInstErrorUnionType(mod, scope, old_inst.castTag(.error_union_type).?),
- .anyframe_type => return analyzeInstAnyframeType(mod, scope, old_inst.castTag(.anyframe_type).?),
- .error_set => return analyzeInstErrorSet(mod, scope, old_inst.castTag(.error_set).?),
- .slice => return analyzeInstSlice(mod, scope, old_inst.castTag(.slice).?),
- .slice_start => return analyzeInstSliceStart(mod, scope, old_inst.castTag(.slice_start).?),
- }
-}
-
-pub fn analyzeBody(mod: *Module, scope: *Scope, body: zir.Module.Body) !void {
- for (body.instructions) |src_inst, i| {
- const analyzed_inst = try analyzeInst(mod, scope, src_inst);
- src_inst.analyzed_inst = analyzed_inst;
- if (analyzed_inst.ty.zigTypeTag() == .NoReturn) {
- for (body.instructions[i..]) |unreachable_inst| {
- if (unreachable_inst.castTag(.dbg_stmt)) |dbg_stmt| {
- return mod.fail(scope, dbg_stmt.base.src, "unreachable code", .{});
- }
- }
- break;
- }
- }
-}
-
-pub fn analyzeBodyValueAsType(
- mod: *Module,
- block_scope: *Scope.Block,
- zir_result_inst: *zir.Inst,
- body: zir.Module.Body,
-) !Type {
- try analyzeBody(mod, &block_scope.base, body);
- const result_inst = zir_result_inst.analyzed_inst.?;
- const val = try mod.resolveConstValue(&block_scope.base, result_inst);
- return val.toType(block_scope.base.arena());
-}
-
-pub fn analyzeZirDecl(mod: *Module, decl: *Decl, src_decl: *zir.Decl) InnerError!bool {
- var decl_scope: Scope.DeclAnalysis = .{
- .decl = decl,
- .arena = std.heap.ArenaAllocator.init(mod.gpa),
- };
- errdefer decl_scope.arena.deinit();
-
- decl.analysis = .in_progress;
-
- const typed_value = try analyzeConstInst(mod, &decl_scope.base, src_decl.inst);
- const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State);
-
- var prev_type_has_bits = false;
- var type_changed = true;
-
- if (decl.typedValueManaged()) |tvm| {
- prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
- type_changed = !tvm.typed_value.ty.eql(typed_value.ty);
-
- tvm.deinit(mod.gpa);
- }
-
- arena_state.* = decl_scope.arena.state;
- decl.typed_value = .{
- .most_recent = .{
- .typed_value = typed_value,
- .arena = arena_state,
- },
- };
- decl.analysis = .complete;
- decl.generation = mod.generation;
- if (typed_value.ty.hasCodeGenBits()) {
- // 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 });
- } else if (prev_type_has_bits) {
- mod.bin_file.freeDecl(decl);
- }
-
- return type_changed;
-}
-
-pub fn resolveZirDecl(mod: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl {
- const zir_module = mod.root_scope.cast(Scope.ZIRModule).?;
- const entry = zir_module.contents.module.findDecl(src_decl.name).?;
- return resolveZirDeclHavingIndex(mod, scope, src_decl, entry.index);
-}
-
-fn resolveZirDeclHavingIndex(mod: *Module, scope: *Scope, src_decl: *zir.Decl, src_index: usize) InnerError!*Decl {
- const name_hash = scope.namespace().fullyQualifiedNameHash(src_decl.name);
- const decl = mod.decl_table.get(name_hash).?;
- decl.src_index = src_index;
- try mod.ensureDeclAnalyzed(decl);
- return decl;
-}
-
-/// Declares a dependency on the decl.
-fn resolveCompleteZirDecl(mod: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl {
- const decl = try resolveZirDecl(mod, scope, src_decl);
- switch (decl.analysis) {
- .unreferenced => unreachable,
- .in_progress => unreachable,
- .outdated => unreachable,
-
- .dependency_failure,
- .sema_failure,
- .sema_failure_retryable,
- .codegen_failure,
- .codegen_failure_retryable,
- => return error.AnalysisFail,
-
- .complete => {},
- }
- return decl;
-}
-
-/// TODO Look into removing this function. The body is only needed for .zir files, not .zig files.
-pub fn resolveInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
- if (old_inst.analyzed_inst) |inst| return inst;
-
- // If this assert trips, the instruction that was referenced did not get properly
- // analyzed before it was referenced.
- const zir_module = scope.namespace().cast(Scope.ZIRModule).?;
- const entry = if (old_inst.cast(zir.Inst.DeclVal)) |declval| blk: {
- const decl_name = declval.positionals.name;
- const entry = zir_module.contents.module.findDecl(decl_name) orelse
- return mod.fail(scope, old_inst.src, "decl '{}' not found", .{decl_name});
- break :blk entry;
- } else blk: {
- // If this assert trips, the instruction that was referenced did not get
- // properly analyzed by a previous instruction analysis before it was
- // referenced by the current one.
- break :blk zir_module.contents.module.findInstDecl(old_inst).?;
- };
- const decl = try resolveCompleteZirDecl(mod, scope, entry.decl);
- const decl_ref = try mod.analyzeDeclRef(scope, old_inst.src, decl);
- // Note: it would be tempting here to store the result into old_inst.analyzed_inst field,
- // but this would prevent the analyzeDeclRef from happening, which is needed to properly
- // detect Decl dependencies and dependency failures on updates.
- return mod.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src);
-}
-
-fn resolveConstString(mod: *Module, scope: *Scope, old_inst: *zir.Inst) ![]u8 {
- const new_inst = try resolveInst(mod, scope, old_inst);
- const wanted_type = Type.initTag(.const_slice_u8);
- const coerced_inst = try mod.coerce(scope, wanted_type, new_inst);
- const val = try mod.resolveConstValue(scope, coerced_inst);
- return val.toAllocatedBytes(scope.arena());
-}
-
-fn resolveType(mod: *Module, scope: *Scope, old_inst: *zir.Inst) !Type {
- const new_inst = try resolveInst(mod, scope, old_inst);
- const wanted_type = Type.initTag(.@"type");
- const coerced_inst = try mod.coerce(scope, wanted_type, new_inst);
- const val = try mod.resolveConstValue(scope, coerced_inst);
- return val.toType(scope.arena());
-}
-
-fn resolveInt(mod: *Module, scope: *Scope, old_inst: *zir.Inst, dest_type: Type) !u64 {
- const new_inst = try resolveInst(mod, scope, old_inst);
- const coerced = try mod.coerce(scope, dest_type, new_inst);
- const val = try mod.resolveConstValue(scope, coerced);
-
- return val.toUnsignedInt();
-}
-
-pub fn resolveInstConst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue {
- const new_inst = try resolveInst(mod, scope, old_inst);
- const val = try mod.resolveConstValue(scope, new_inst);
- return TypedValue{
- .ty = new_inst.ty,
- .val = val,
- };
-}
-
-fn analyzeInstConst(mod: *Module, scope: *Scope, const_inst: *zir.Inst.Const) InnerError!*Inst {
- // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions
- // after analysis.
- const typed_value_copy = try const_inst.positionals.typed_value.copy(scope.arena());
- return mod.constInst(scope, const_inst.base.src, typed_value_copy);
-}
-
-fn analyzeConstInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue {
- const new_inst = try analyzeInst(mod, scope, old_inst);
- return TypedValue{
- .ty = new_inst.ty,
- .val = try mod.resolveConstValue(scope, new_inst),
- };
-}
-
-fn analyzeInstCoerceResultBlockPtr(
- mod: *Module,
- scope: *Scope,
- inst: *zir.Inst.CoerceResultBlockPtr,
-) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultBlockPtr", .{});
-}
-
-fn analyzeInstBitCastRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastRef", .{});
-}
-
-fn analyzeInstBitCastResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastResultPtr", .{});
-}
-
-fn analyzeInstCoerceResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultPtr", .{});
-}
-
-/// Equivalent to `as(ptr_child_type(typeof(ptr)), value)`.
-fn analyzeInstCoerceToPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.CoerceToPtrElem) InnerError!*Inst {
- const ptr = try resolveInst(mod, scope, inst.positionals.ptr);
- const operand = try resolveInst(mod, scope, inst.positionals.value);
- return mod.coerce(scope, ptr.ty.elemType(), operand);
-}
-
-fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstRetPtr", .{});
-}
-
-fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- const operand = try resolveInst(mod, scope, inst.positionals.operand);
- const ptr_type = try mod.simplePtrType(scope, inst.base.src, operand.ty, false, .One);
-
- if (operand.value()) |val| {
- const ref_payload = try scope.arena().create(Value.Payload.RefVal);
- ref_payload.* = .{ .val = val };
-
- return mod.constInst(scope, inst.base.src, .{
- .ty = ptr_type,
- .val = Value.initPayload(&ref_payload.base),
- });
- }
-
- const b = try mod.requireRuntimeBlock(scope, inst.base.src);
- return mod.addUnOp(b, inst.base.src, ptr_type, .ref, operand);
-}
-
-fn analyzeInstRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
- const b = try mod.requireFunctionBlock(scope, inst.base.src);
- const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
- const ret_type = fn_ty.fnReturnType();
- return mod.constType(scope, inst.base.src, ret_type);
-}
-
-fn analyzeInstEnsureResultUsed(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- const operand = try resolveInst(mod, scope, inst.positionals.operand);
- switch (operand.ty.zigTypeTag()) {
- .Void, .NoReturn => return mod.constVoid(scope, operand.src),
- else => return mod.fail(scope, operand.src, "expression value is ignored", .{}),
- }
-}
-
-fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- const operand = try resolveInst(mod, scope, inst.positionals.operand);
- switch (operand.ty.zigTypeTag()) {
- .ErrorSet, .ErrorUnion => return mod.fail(scope, operand.src, "error is discarded", .{}),
- else => return mod.constVoid(scope, operand.src),
- }
-}
-
-fn analyzeInstEnsureIndexable(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- const operand = try resolveInst(mod, scope, inst.positionals.operand);
- const elem_ty = operand.ty.elemType();
- if (elem_ty.isIndexable()) {
- return mod.constVoid(scope, operand.src);
- } else {
- // TODO error notes
- // error: type '{}' does not support indexing
- // note: for loop operand must be an array, a slice or a tuple
- return mod.fail(scope, operand.src, "for loop operand must be an array, a slice or a tuple", .{});
- }
-}
-
-fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- const var_type = try resolveType(mod, scope, inst.positionals.operand);
- // TODO this should happen only for var allocs
- if (!var_type.isValidVarType(false)) {
- return mod.fail(scope, inst.base.src, "variable of type '{}' must be const or comptime", .{var_type});
- }
- const ptr_type = try mod.simplePtrType(scope, inst.base.src, var_type, true, .One);
- const b = try mod.requireRuntimeBlock(scope, inst.base.src);
- return mod.addNoOp(b, inst.base.src, ptr_type, .alloc);
-}
-
-fn analyzeInstAllocInferred(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferred", .{});
-}
-
-fn analyzeInstStore(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- const ptr = try resolveInst(mod, scope, inst.positionals.lhs);
- const value = try resolveInst(mod, scope, inst.positionals.rhs);
- return mod.storePtr(scope, inst.base.src, ptr, value);
-}
-
-fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) InnerError!*Inst {
- const fn_inst = try resolveInst(mod, scope, inst.positionals.func);
- const arg_index = inst.positionals.arg_index;
-
- const fn_ty: Type = switch (fn_inst.ty.zigTypeTag()) {
- .Fn => fn_inst.ty,
- .BoundFn => {
- return mod.fail(scope, fn_inst.src, "TODO implement analyzeInstParamType for method call syntax", .{});
- },
- else => {
- return mod.fail(scope, fn_inst.src, "expected function, found '{}'", .{fn_inst.ty});
- },
- };
-
- // TODO support C-style var args
- const param_count = fn_ty.fnParamLen();
- if (arg_index >= param_count) {
- return mod.fail(scope, inst.base.src, "arg index {} out of bounds; '{}' has {} argument(s)", .{
- arg_index,
- fn_ty,
- param_count,
- });
- }
-
- // TODO support generic functions
- const param_type = fn_ty.fnParamType(arg_index);
- return mod.constType(scope, inst.base.src, param_type);
-}
-
-fn analyzeInstStr(mod: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerError!*Inst {
- // The bytes references memory inside the ZIR module, which can get deallocated
- // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena.
- var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
- errdefer new_decl_arena.deinit();
- const arena_bytes = try new_decl_arena.allocator.dupe(u8, str_inst.positionals.bytes);
-
- const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0);
- ty_payload.* = .{ .len = arena_bytes.len };
-
- const bytes_payload = try scope.arena().create(Value.Payload.Bytes);
- bytes_payload.* = .{ .data = arena_bytes };
-
- const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{
- .ty = Type.initPayload(&ty_payload.base),
- .val = Value.initPayload(&bytes_payload.base),
- });
- return mod.analyzeDeclRef(scope, str_inst.base.src, new_decl);
-}
-
-fn analyzeInstExport(mod: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!*Inst {
- const symbol_name = try resolveConstString(mod, scope, export_inst.positionals.symbol_name);
- const exported_decl = mod.lookupDeclName(scope, export_inst.positionals.decl_name) orelse
- return mod.fail(scope, export_inst.base.src, "decl '{}' not found", .{export_inst.positionals.decl_name});
- try mod.analyzeExport(scope, export_inst.base.src, symbol_name, exported_decl);
- return mod.constVoid(scope, export_inst.base.src);
-}
-
-fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.CompileError) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "{}", .{inst.positionals.msg});
-}
-
-fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst {
- const b = try mod.requireRuntimeBlock(scope, inst.base.src);
- const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
- const param_index = b.instructions.items.len;
- const param_count = fn_ty.fnParamLen();
- if (param_index >= param_count) {
- return mod.fail(scope, inst.base.src, "parameter index {} outside list of length {}", .{
- param_index,
- param_count,
- });
- }
- const param_type = fn_ty.fnParamType(param_index);
- const name = try scope.arena().dupeZ(u8, inst.positionals.name);
- return mod.addArg(b, inst.base.src, param_type, name);
-}
-
-fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError!*Inst {
- const parent_block = scope.cast(Scope.Block).?;
-
- // Reserve space for a Loop instruction so that generated Break instructions can
- // point to it, even if it doesn't end up getting used because the code ends up being
- // comptime evaluated.
- const loop_inst = try parent_block.arena.create(Inst.Loop);
- loop_inst.* = .{
- .base = .{
- .tag = Inst.Loop.base_tag,
- .ty = Type.initTag(.noreturn),
- .src = inst.base.src,
- },
- .body = undefined,
- };
-
- var child_block: Scope.Block = .{
- .parent = parent_block,
- .func = parent_block.func,
- .decl = parent_block.decl,
- .instructions = .{},
- .arena = parent_block.arena,
- .is_comptime = parent_block.is_comptime,
- };
- defer child_block.instructions.deinit(mod.gpa);
-
- try analyzeBody(mod, &child_block.base, inst.positionals.body);
-
- // Loop repetition is implied so the last instruction may or may not be a noreturn instruction.
-
- try parent_block.instructions.append(mod.gpa, &loop_inst.base);
- loop_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
- return &loop_inst.base;
-}
-
-fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: bool) InnerError!*Inst {
- const parent_block = scope.cast(Scope.Block).?;
-
- var child_block: Scope.Block = .{
- .parent = parent_block,
- .func = parent_block.func,
- .decl = parent_block.decl,
- .instructions = .{},
- .arena = parent_block.arena,
- .label = null,
- .is_comptime = parent_block.is_comptime or is_comptime,
- };
- defer child_block.instructions.deinit(mod.gpa);
-
- try analyzeBody(mod, &child_block.base, inst.positionals.body);
-
- const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items);
- try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
-
- return copied_instructions[copied_instructions.len - 1];
-}
-
-fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: bool) InnerError!*Inst {
- const parent_block = scope.cast(Scope.Block).?;
-
- // Reserve space for a Block instruction so that generated Break instructions can
- // point to it, even if it doesn't end up getting used because the code ends up being
- // comptime evaluated.
- const block_inst = try parent_block.arena.create(Inst.Block);
- block_inst.* = .{
- .base = .{
- .tag = Inst.Block.base_tag,
- .ty = undefined, // Set after analysis.
- .src = inst.base.src,
- },
- .body = undefined,
- };
-
- var child_block: Scope.Block = .{
- .parent = parent_block,
- .func = parent_block.func,
- .decl = parent_block.decl,
- .instructions = .{},
- .arena = parent_block.arena,
- // TODO @as here is working around a stage1 miscompilation bug :(
- .label = @as(?Scope.Block.Label, Scope.Block.Label{
- .zir_block = inst,
- .results = .{},
- .block_inst = block_inst,
- }),
- .is_comptime = is_comptime or parent_block.is_comptime,
- };
- const label = &child_block.label.?;
-
- defer child_block.instructions.deinit(mod.gpa);
- defer label.results.deinit(mod.gpa);
-
- try analyzeBody(mod, &child_block.base, inst.positionals.body);
-
- // Blocks must terminate with noreturn instruction.
- assert(child_block.instructions.items.len != 0);
- assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn());
-
- if (label.results.items.len == 0) {
- // No need for a block instruction. We can put the new instructions directly into the parent block.
- const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items);
- try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
- return copied_instructions[copied_instructions.len - 1];
- }
- if (label.results.items.len == 1) {
- const last_inst_index = child_block.instructions.items.len - 1;
- const last_inst = child_block.instructions.items[last_inst_index];
- if (last_inst.breakBlock()) |br_block| {
- if (br_block == block_inst) {
- // No need for a block instruction. We can put the new instructions directly into the parent block.
- // Here we omit the break instruction.
- const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]);
- try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
- return label.results.items[0];
- }
- }
- }
- // It should be impossible to have the number of results be > 1 in a comptime scope.
- assert(!child_block.is_comptime); // We should have already got a compile error in the condbr condition.
-
- // Need to set the type and emit the Block instruction. This allows machine code generation
- // to emit a jump instruction to after the block when it encounters the break.
- try parent_block.instructions.append(mod.gpa, &block_inst.base);
- block_inst.base.ty = try mod.resolvePeerTypes(scope, label.results.items);
- block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
- return &block_inst.base;
-}
-
-fn analyzeInstBreakpoint(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
- const b = try mod.requireRuntimeBlock(scope, inst.base.src);
- return mod.addNoOp(b, inst.base.src, Type.initTag(.void), .breakpoint);
-}
-
-fn analyzeInstBreak(mod: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst {
- const operand = try resolveInst(mod, scope, inst.positionals.operand);
- const block = inst.positionals.block;
- return analyzeBreak(mod, scope, inst.base.src, block, operand);
-}
-
-fn analyzeInstBreakVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.BreakVoid) InnerError!*Inst {
- const block = inst.positionals.block;
- const void_inst = try mod.constVoid(scope, inst.base.src);
- return analyzeBreak(mod, scope, inst.base.src, block, void_inst);
-}
-
-fn analyzeInstDbgStmt(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
- if (scope.cast(Scope.Block)) |b| {
- if (!b.is_comptime) {
- return mod.addNoOp(b, inst.base.src, Type.initTag(.void), .dbg_stmt);
- }
- }
- return mod.constVoid(scope, inst.base.src);
-}
-
-fn analyzeInstDeclRefStr(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr) InnerError!*Inst {
- const decl_name = try resolveConstString(mod, scope, inst.positionals.name);
- return mod.analyzeDeclRefByName(scope, inst.base.src, decl_name);
-}
-
-fn analyzeInstDeclRef(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst {
- return mod.analyzeDeclRefByName(scope, inst.base.src, inst.positionals.name);
-}
-
-fn analyzeInstDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Inst {
- const decl = try analyzeDeclVal(mod, scope, inst);
- const ptr = try mod.analyzeDeclRef(scope, inst.base.src, decl);
- return mod.analyzeDeref(scope, inst.base.src, ptr, inst.base.src);
-}
-
-fn analyzeInstDeclValInModule(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclValInModule) InnerError!*Inst {
- const decl = inst.positionals.decl;
- return mod.analyzeDeclRef(scope, inst.base.src, decl);
-}
-
-fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst {
- const func = try resolveInst(mod, scope, inst.positionals.func);
- if (func.ty.zigTypeTag() != .Fn)
- return mod.fail(scope, inst.positionals.func.src, "type '{}' not a function", .{func.ty});
-
- const cc = func.ty.fnCallingConvention();
- if (cc == .Naked) {
- // TODO add error note: declared here
- return mod.fail(
- scope,
- inst.positionals.func.src,
- "unable to call function with naked calling convention",
- .{},
- );
- }
- const call_params_len = inst.positionals.args.len;
- const fn_params_len = func.ty.fnParamLen();
- if (func.ty.fnIsVarArgs()) {
- if (call_params_len < fn_params_len) {
- // TODO add error note: declared here
- return mod.fail(
- scope,
- inst.positionals.func.src,
- "expected at least {} argument(s), found {}",
- .{ fn_params_len, call_params_len },
- );
- }
- return mod.fail(scope, inst.base.src, "TODO implement support for calling var args functions", .{});
- } else if (fn_params_len != call_params_len) {
- // TODO add error note: declared here
- return mod.fail(
- scope,
- inst.positionals.func.src,
- "expected {} argument(s), found {}",
- .{ fn_params_len, call_params_len },
- );
- }
-
- if (inst.kw_args.modifier == .compile_time) {
- return mod.fail(scope, inst.base.src, "TODO implement comptime function calls", .{});
- }
- if (inst.kw_args.modifier != .auto) {
- return mod.fail(scope, inst.base.src, "TODO implement call with modifier {}", .{inst.kw_args.modifier});
- }
-
- // TODO handle function calls of generic functions
-
- const fn_param_types = try mod.gpa.alloc(Type, fn_params_len);
- defer mod.gpa.free(fn_param_types);
- func.ty.fnParamTypes(fn_param_types);
-
- const casted_args = try scope.arena().alloc(*Inst, fn_params_len);
- for (inst.positionals.args) |src_arg, i| {
- const uncasted_arg = try resolveInst(mod, scope, src_arg);
- casted_args[i] = try mod.coerce(scope, fn_param_types[i], uncasted_arg);
- }
-
- const ret_type = func.ty.fnReturnType();
-
- const b = try mod.requireRuntimeBlock(scope, inst.base.src);
- return mod.addCall(b, inst.base.src, ret_type, func, casted_args);
-}
-
-fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst {
- const fn_type = try resolveType(mod, scope, fn_inst.positionals.fn_type);
- const fn_zir = blk: {
- var fn_arena = std.heap.ArenaAllocator.init(mod.gpa);
- errdefer fn_arena.deinit();
-
- const fn_zir = try scope.arena().create(Module.Fn.ZIR);
- fn_zir.* = .{
- .body = .{
- .instructions = fn_inst.positionals.body.instructions,
- },
- .arena = fn_arena.state,
- };
- break :blk fn_zir;
- };
- const new_func = try scope.arena().create(Module.Fn);
- new_func.* = .{
- .analysis = .{ .queued = fn_zir },
- .owner_decl = scope.decl().?,
- };
- const fn_payload = try scope.arena().create(Value.Payload.Function);
- fn_payload.* = .{ .func = new_func };
- return mod.constInst(scope, fn_inst.base.src, .{
- .ty = fn_type,
- .val = Value.initPayload(&fn_payload.base),
- });
-}
-
-fn analyzeInstIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) InnerError!*Inst {
- return mod.fail(scope, inttype.base.src, "TODO implement inttype", .{});
-}
-
-fn analyzeInstOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp) InnerError!*Inst {
- const child_type = try resolveType(mod, scope, optional.positionals.operand);
-
- return mod.constType(scope, optional.base.src, try mod.optionalType(scope, child_type));
-}
-
-fn analyzeInstArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst {
- // TODO these should be lazily evaluated
- const len = try resolveInstConst(mod, scope, array.positionals.lhs);
- const elem_type = try resolveType(mod, scope, array.positionals.rhs);
-
- return mod.constType(scope, array.base.src, try mod.arrayType(scope, len.val.toUnsignedInt(), null, elem_type));
-}
-
-fn analyzeInstArrayTypeSentinel(mod: *Module, scope: *Scope, array: *zir.Inst.ArrayTypeSentinel) InnerError!*Inst {
- // TODO these should be lazily evaluated
- const len = try resolveInstConst(mod, scope, array.positionals.len);
- const sentinel = try resolveInstConst(mod, scope, array.positionals.sentinel);
- const elem_type = try resolveType(mod, scope, array.positionals.elem_type);
-
- return mod.constType(scope, array.base.src, try mod.arrayType(scope, len.val.toUnsignedInt(), sentinel.val, elem_type));
-}
-
-fn analyzeInstErrorUnionType(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- const error_union = try resolveType(mod, scope, inst.positionals.lhs);
- const payload = try resolveType(mod, scope, inst.positionals.rhs);
-
- if (error_union.zigTypeTag() != .ErrorSet) {
- return mod.fail(scope, inst.base.src, "expected error set type, found {}", .{error_union.elemType()});
- }
-
- return mod.constType(scope, inst.base.src, try mod.errorUnionType(scope, error_union, payload));
-}
-
-fn analyzeInstAnyframeType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- const return_type = try resolveType(mod, scope, inst.positionals.operand);
-
- return mod.constType(scope, inst.base.src, try mod.anyframeType(scope, return_type));
-}
-
-fn analyzeInstErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) InnerError!*Inst {
- // The declarations arena will store the hashmap.
- var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
- errdefer new_decl_arena.deinit();
-
- const payload = try scope.arena().create(Value.Payload.ErrorSet);
- payload.* = .{
- .fields = .{},
- .decl = undefined, // populated below
- };
- try payload.fields.ensureCapacity(&new_decl_arena.allocator, @intCast(u32, inst.positionals.fields.len));
-
- for (inst.positionals.fields) |field_name| {
- const entry = try mod.getErrorValue(field_name);
- if (payload.fields.fetchPutAssumeCapacity(entry.key, entry.value)) |prev| {
- return mod.fail(scope, inst.base.src, "duplicate error: '{}'", .{field_name});
- }
- }
- // TODO create name in format "error:line:column"
- const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{
- .ty = Type.initTag(.type),
- .val = Value.initPayload(&payload.base),
- });
- payload.decl = new_decl;
- return mod.analyzeDeclRef(scope, inst.base.src, new_decl);
-}
-
-fn analyzeInstMergeErrorSets(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement merge_error_sets", .{});
-}
-
-fn analyzeInstEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiteral) InnerError!*Inst {
- const payload = try scope.arena().create(Value.Payload.Bytes);
- payload.* = .{
- .base = .{ .tag = .enum_literal },
- .data = try scope.arena().dupe(u8, inst.positionals.name),
- };
- return mod.constInst(scope, inst.base.src, .{
- .ty = Type.initTag(.enum_literal),
- .val = Value.initPayload(&payload.base),
- });
-}
-
-fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst {
- const operand = try resolveInst(mod, scope, unwrap.positionals.operand);
- assert(operand.ty.zigTypeTag() == .Pointer);
-
- const elem_type = operand.ty.elemType();
- if (elem_type.zigTypeTag() != .Optional) {
- return mod.fail(scope, unwrap.base.src, "expected optional type, found {}", .{elem_type});
- }
-
- const child_type = try elem_type.optionalChildAlloc(scope.arena());
- const child_pointer = try mod.simplePtrType(scope, unwrap.base.src, child_type, operand.ty.isConstPtr(), .One);
-
- if (operand.value()) |val| {
- if (val.isNull()) {
- return mod.fail(scope, unwrap.base.src, "unable to unwrap null", .{});
- }
- return mod.constInst(scope, unwrap.base.src, .{
- .ty = child_pointer,
- .val = val,
- });
- }
-
- const b = try mod.requireRuntimeBlock(scope, unwrap.base.src);
- if (safety_check and mod.wantSafety(scope)) {
- const is_non_null = try mod.addUnOp(b, unwrap.base.src, Type.initTag(.bool), .isnonnull, operand);
- try mod.addSafetyCheck(b, is_non_null, .unwrap_null);
- }
- return mod.addUnOp(b, unwrap.base.src, child_pointer, .unwrap_optional, operand);
-}
-
-fn analyzeInstUnwrapErr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst {
- return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstUnwrapErr", .{});
-}
-
-fn analyzeInstUnwrapErrCode(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst {
- return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstUnwrapErrCode", .{});
-}
-
-fn analyzeInstEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst {
- return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstEnsureErrPayloadVoid", .{});
-}
-
-fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {
- const return_type = try resolveType(mod, scope, fntype.positionals.return_type);
-
- // Hot path for some common function types.
- if (fntype.positionals.param_types.len == 0) {
- if (return_type.zigTypeTag() == .NoReturn and fntype.kw_args.cc == .Unspecified) {
- return mod.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args));
- }
-
- if (return_type.zigTypeTag() == .Void and fntype.kw_args.cc == .Unspecified) {
- return mod.constType(scope, fntype.base.src, Type.initTag(.fn_void_no_args));
- }
-
- if (return_type.zigTypeTag() == .NoReturn and fntype.kw_args.cc == .Naked) {
- return mod.constType(scope, fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args));
- }
-
- if (return_type.zigTypeTag() == .Void and fntype.kw_args.cc == .C) {
- return mod.constType(scope, fntype.base.src, Type.initTag(.fn_ccc_void_no_args));
- }
- }
-
- const arena = scope.arena();
- const param_types = try arena.alloc(Type, fntype.positionals.param_types.len);
- for (fntype.positionals.param_types) |param_type, i| {
- const resolved = try resolveType(mod, scope, param_type);
- // TODO skip for comptime params
- if (!resolved.isValidVarType(false)) {
- return mod.fail(scope, param_type.src, "parameter of type '{}' must be declared comptime", .{resolved});
- }
- param_types[i] = resolved;
- }
-
- const payload = try arena.create(Type.Payload.Function);
- payload.* = .{
- .cc = fntype.kw_args.cc,
- .return_type = return_type,
- .param_types = param_types,
- };
- return mod.constType(scope, fntype.base.src, Type.initPayload(&payload.base));
-}
-
-fn analyzeInstPrimitive(mod: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst {
- return mod.constInst(scope, primitive.base.src, primitive.positionals.tag.toTypedValue());
-}
-
-fn analyzeInstAs(mod: *Module, scope: *Scope, as: *zir.Inst.BinOp) InnerError!*Inst {
- const dest_type = try resolveType(mod, scope, as.positionals.lhs);
- const new_inst = try resolveInst(mod, scope, as.positionals.rhs);
- return mod.coerce(scope, dest_type, new_inst);
-}
-
-fn analyzeInstPtrToInt(mod: *Module, scope: *Scope, ptrtoint: *zir.Inst.UnOp) InnerError!*Inst {
- const ptr = try resolveInst(mod, scope, ptrtoint.positionals.operand);
- if (ptr.ty.zigTypeTag() != .Pointer) {
- return mod.fail(scope, ptrtoint.positionals.operand.src, "expected pointer, found '{}'", .{ptr.ty});
- }
- // TODO handle known-pointer-address
- const b = try mod.requireRuntimeBlock(scope, ptrtoint.base.src);
- const ty = Type.initTag(.usize);
- return mod.addUnOp(b, ptrtoint.base.src, ty, .ptrtoint, ptr);
-}
-
-fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst {
- const object_ptr = try resolveInst(mod, scope, fieldptr.positionals.object_ptr);
- const field_name = try resolveConstString(mod, scope, fieldptr.positionals.field_name);
-
- const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
- .Pointer => object_ptr.ty.elemType(),
- else => return mod.fail(scope, fieldptr.positionals.object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
- };
- switch (elem_ty.zigTypeTag()) {
- .Array => {
- if (mem.eql(u8, field_name, "len")) {
- const len_payload = try scope.arena().create(Value.Payload.Int_u64);
- len_payload.* = .{ .int = elem_ty.arrayLen() };
-
- const ref_payload = try scope.arena().create(Value.Payload.RefVal);
- ref_payload.* = .{ .val = Value.initPayload(&len_payload.base) };
-
- return mod.constInst(scope, fieldptr.base.src, .{
- .ty = Type.initTag(.single_const_pointer_to_comptime_int),
- .val = Value.initPayload(&ref_payload.base),
- });
- } else {
- return mod.fail(
- scope,
- fieldptr.positionals.field_name.src,
- "no member named '{}' in '{}'",
- .{ field_name, elem_ty },
- );
- }
- },
- .Pointer => {
- const ptr_child = elem_ty.elemType();
- switch (ptr_child.zigTypeTag()) {
- .Array => {
- if (mem.eql(u8, field_name, "len")) {
- const len_payload = try scope.arena().create(Value.Payload.Int_u64);
- len_payload.* = .{ .int = ptr_child.arrayLen() };
-
- const ref_payload = try scope.arena().create(Value.Payload.RefVal);
- ref_payload.* = .{ .val = Value.initPayload(&len_payload.base) };
-
- return mod.constInst(scope, fieldptr.base.src, .{
- .ty = Type.initTag(.single_const_pointer_to_comptime_int),
- .val = Value.initPayload(&ref_payload.base),
- });
- } else {
- return mod.fail(
- scope,
- fieldptr.positionals.field_name.src,
- "no member named '{}' in '{}'",
- .{ field_name, elem_ty },
- );
- }
- },
- else => {},
- }
- },
- .Type => {
- _ = try mod.resolveConstValue(scope, object_ptr);
- const result = try mod.analyzeDeref(scope, fieldptr.base.src, object_ptr, object_ptr.src);
- const val = result.value().?;
- const child_type = try val.toType(scope.arena());
- switch (child_type.zigTypeTag()) {
- .ErrorSet => {
- // TODO resolve inferred error sets
- const entry = if (val.cast(Value.Payload.ErrorSet)) |payload|
- (payload.fields.getEntry(field_name) orelse
- return mod.fail(scope, fieldptr.base.src, "no error named '{}' in '{}'", .{ field_name, child_type })).*
- else
- try mod.getErrorValue(field_name);
-
- const error_payload = try scope.arena().create(Value.Payload.Error);
- error_payload.* = .{
- .name = entry.key,
- .value = entry.value,
- };
-
- const ref_payload = try scope.arena().create(Value.Payload.RefVal);
- ref_payload.* = .{ .val = Value.initPayload(&error_payload.base) };
-
- const result_type = if (child_type.tag() == .anyerror) blk: {
- const result_payload = try scope.arena().create(Type.Payload.ErrorSetSingle);
- result_payload.* = .{ .name = entry.key };
- break :blk Type.initPayload(&result_payload.base);
- } else child_type;
-
- return mod.constInst(scope, fieldptr.base.src, .{
- .ty = try mod.simplePtrType(scope, fieldptr.base.src, result_type, false, .One),
- .val = Value.initPayload(&ref_payload.base),
- });
- },
- else => return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{child_type}),
- }
- },
- else => {},
- }
- return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{elem_ty});
-}
-
-fn analyzeInstIntCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- const dest_type = try resolveType(mod, scope, inst.positionals.lhs);
- const operand = try resolveInst(mod, scope, inst.positionals.rhs);
-
- const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
- .ComptimeInt => true,
- .Int => false,
- else => return mod.fail(
- scope,
- inst.positionals.lhs.src,
- "expected integer type, found '{}'",
- .{
- dest_type,
- },
- ),
- };
-
- switch (operand.ty.zigTypeTag()) {
- .ComptimeInt, .Int => {},
- else => return mod.fail(
- scope,
- inst.positionals.rhs.src,
- "expected integer type, found '{}'",
- .{operand.ty},
- ),
- }
-
- if (operand.value() != null) {
- return mod.coerce(scope, dest_type, operand);
- } else if (dest_is_comptime_int) {
- return mod.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_int'", .{});
- }
-
- return mod.fail(scope, inst.base.src, "TODO implement analyze widen or shorten int", .{});
-}
-
-fn analyzeInstBitCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- const dest_type = try resolveType(mod, scope, inst.positionals.lhs);
- const operand = try resolveInst(mod, scope, inst.positionals.rhs);
- return mod.bitcast(scope, dest_type, operand);
-}
-
-fn analyzeInstFloatCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- const dest_type = try resolveType(mod, scope, inst.positionals.lhs);
- const operand = try resolveInst(mod, scope, inst.positionals.rhs);
-
- const dest_is_comptime_float = switch (dest_type.zigTypeTag()) {
- .ComptimeFloat => true,
- .Float => false,
- else => return mod.fail(
- scope,
- inst.positionals.lhs.src,
- "expected float type, found '{}'",
- .{
- dest_type,
- },
- ),
- };
-
- switch (operand.ty.zigTypeTag()) {
- .ComptimeFloat, .Float, .ComptimeInt => {},
- else => return mod.fail(
- scope,
- inst.positionals.rhs.src,
- "expected float type, found '{}'",
- .{operand.ty},
- ),
- }
-
- if (operand.value() != null) {
- return mod.coerce(scope, dest_type, operand);
- } else if (dest_is_comptime_float) {
- return mod.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_float'", .{});
- }
-
- return mod.fail(scope, inst.base.src, "TODO implement analyze widen or shorten float", .{});
-}
-
-fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) InnerError!*Inst {
- const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr);
- const uncasted_index = try resolveInst(mod, scope, inst.positionals.index);
- const elem_index = try mod.coerce(scope, Type.initTag(.usize), uncasted_index);
-
- const elem_ty = switch (array_ptr.ty.zigTypeTag()) {
- .Pointer => array_ptr.ty.elemType(),
- else => return mod.fail(scope, inst.positionals.array_ptr.src, "expected pointer, found '{}'", .{array_ptr.ty}),
- };
- if (!elem_ty.isIndexable()) {
- return mod.fail(scope, inst.base.src, "array access of non-array type '{}'", .{elem_ty});
- }
-
- if (elem_ty.isSinglePointer() and elem_ty.elemType().zigTypeTag() == .Array) {
- // we have to deref the ptr operand to get the actual array pointer
- const array_ptr_deref = try mod.analyzeDeref(scope, inst.base.src, array_ptr, inst.positionals.array_ptr.src);
- if (array_ptr_deref.value()) |array_ptr_val| {
- if (elem_index.value()) |index_val| {
- // Both array pointer and index are compile-time known.
- const index_u64 = index_val.toUnsignedInt();
- // @intCast here because it would have been impossible to construct a value that
- // required a larger index.
- const elem_ptr = try array_ptr_val.elemPtr(scope.arena(), @intCast(usize, index_u64));
-
- const type_payload = try scope.arena().create(Type.Payload.PointerSimple);
- type_payload.* = .{
- .base = .{ .tag = .single_const_pointer },
- .pointee_type = elem_ty.elemType().elemType(),
- };
-
- return mod.constInst(scope, inst.base.src, .{
- .ty = Type.initPayload(&type_payload.base),
- .val = elem_ptr,
- });
- }
- }
- }
-
- return mod.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{});
-}
-
-fn analyzeInstSlice(mod: *Module, scope: *Scope, inst: *zir.Inst.Slice) InnerError!*Inst {
- const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr);
- const start = try resolveInst(mod, scope, inst.positionals.start);
- const end = if (inst.kw_args.end) |end| try resolveInst(mod, scope, end) else null;
- const sentinel = if (inst.kw_args.sentinel) |sentinel| try resolveInst(mod, scope, sentinel) else null;
-
- return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, end, sentinel);
-}
-
-fn analyzeInstSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- const array_ptr = try resolveInst(mod, scope, inst.positionals.lhs);
- const start = try resolveInst(mod, scope, inst.positionals.rhs);
-
- return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, null, null);
-}
-
-fn analyzeInstShl(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShl", .{});
-}
-
-fn analyzeInstShr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShr", .{});
-}
-
-fn analyzeInstBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitwise", .{});
-}
-
-fn analyzeInstBitNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitNot", .{});
-}
-
-fn analyzeInstArrayCat(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayCat", .{});
-}
-
-fn analyzeInstArrayMul(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayMul", .{});
-}
-
-fn analyzeInstArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- const tracy = trace(@src());
- defer tracy.end();
-
- const lhs = try resolveInst(mod, scope, inst.positionals.lhs);
- const rhs = try resolveInst(mod, scope, inst.positionals.rhs);
-
- const instructions = &[_]*Inst{ lhs, rhs };
- const resolved_type = try mod.resolvePeerTypes(scope, instructions);
- const casted_lhs = try mod.coerce(scope, resolved_type, lhs);
- const casted_rhs = try mod.coerce(scope, resolved_type, rhs);
-
- const scalar_type = if (resolved_type.zigTypeTag() == .Vector)
- resolved_type.elemType()
- else
- resolved_type;
-
- const scalar_tag = scalar_type.zigTypeTag();
-
- if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) {
- if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
- return mod.fail(scope, inst.base.src, "vector length mismatch: {} and {}", .{
- lhs.ty.arrayLen(),
- rhs.ty.arrayLen(),
- });
- }
- return mod.fail(scope, inst.base.src, "TODO implement support for vectors in analyzeInstBinOp", .{});
- } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) {
- return mod.fail(scope, inst.base.src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
- lhs.ty,
- rhs.ty,
- });
- }
-
- const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
- const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat;
-
- if (!is_int and !(is_float and floatOpAllowed(inst.base.tag))) {
- return mod.fail(scope, inst.base.src, "invalid operands to binary expression: '{}' and '{}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) });
- }
-
- if (casted_lhs.value()) |lhs_val| {
- if (casted_rhs.value()) |rhs_val| {
- if (lhs_val.isUndef() or rhs_val.isUndef()) {
- return mod.constInst(scope, inst.base.src, .{
- .ty = resolved_type,
- .val = Value.initTag(.undef),
- });
- }
- return analyzeInstComptimeOp(mod, scope, scalar_type, inst, lhs_val, rhs_val);
- }
- }
-
- const b = try mod.requireRuntimeBlock(scope, inst.base.src);
- const ir_tag = switch (inst.base.tag) {
- .add => Inst.Tag.add,
- .sub => Inst.Tag.sub,
- else => return mod.fail(scope, inst.base.src, "TODO implement arithmetic for operand '{}''", .{@tagName(inst.base.tag)}),
- };
-
- return mod.addBinOp(b, inst.base.src, scalar_type, ir_tag, casted_lhs, casted_rhs);
-}
-
-/// Analyzes operands that are known at comptime
-fn analyzeInstComptimeOp(mod: *Module, scope: *Scope, res_type: Type, inst: *zir.Inst.BinOp, lhs_val: Value, rhs_val: Value) InnerError!*Inst {
- // incase rhs is 0, simply return lhs without doing any calculations
- // TODO Once division is implemented we should throw an error when dividing by 0.
- if (rhs_val.compareWithZero(.eq)) {
- return mod.constInst(scope, inst.base.src, .{
- .ty = res_type,
- .val = lhs_val,
- });
- }
- const is_int = res_type.isInt() or res_type.zigTypeTag() == .ComptimeInt;
-
- const value = try switch (inst.base.tag) {
- .add => blk: {
- const val = if (is_int)
- Module.intAdd(scope.arena(), lhs_val, rhs_val)
- else
- mod.floatAdd(scope, res_type, inst.base.src, lhs_val, rhs_val);
- break :blk val;
- },
- .sub => blk: {
- const val = if (is_int)
- Module.intSub(scope.arena(), lhs_val, rhs_val)
- else
- mod.floatSub(scope, res_type, inst.base.src, lhs_val, rhs_val);
- break :blk val;
- },
- else => return mod.fail(scope, inst.base.src, "TODO Implement arithmetic operand '{}'", .{@tagName(inst.base.tag)}),
- };
-
- return mod.constInst(scope, inst.base.src, .{
- .ty = res_type,
- .val = value,
- });
-}
-
-fn analyzeInstDeref(mod: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst {
- const ptr = try resolveInst(mod, scope, deref.positionals.operand);
- return mod.analyzeDeref(scope, deref.base.src, ptr, deref.positionals.operand.src);
-}
-
-fn analyzeInstAsm(mod: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerError!*Inst {
- const return_type = try resolveType(mod, scope, assembly.positionals.return_type);
- const asm_source = try resolveConstString(mod, scope, assembly.positionals.asm_source);
- const output = if (assembly.kw_args.output) |o| try resolveConstString(mod, scope, o) else null;
-
- const inputs = try scope.arena().alloc([]const u8, assembly.kw_args.inputs.len);
- const clobbers = try scope.arena().alloc([]const u8, assembly.kw_args.clobbers.len);
- const args = try scope.arena().alloc(*Inst, assembly.kw_args.args.len);
-
- for (inputs) |*elem, i| {
- elem.* = try resolveConstString(mod, scope, assembly.kw_args.inputs[i]);
- }
- for (clobbers) |*elem, i| {
- elem.* = try resolveConstString(mod, scope, assembly.kw_args.clobbers[i]);
- }
- for (args) |*elem, i| {
- const arg = try resolveInst(mod, scope, assembly.kw_args.args[i]);
- elem.* = try mod.coerce(scope, Type.initTag(.usize), arg);
- }
-
- const b = try mod.requireRuntimeBlock(scope, assembly.base.src);
- const inst = try b.arena.create(Inst.Assembly);
- inst.* = .{
- .base = .{
- .tag = .assembly,
- .ty = return_type,
- .src = assembly.base.src,
- },
- .asm_source = asm_source,
- .is_volatile = assembly.kw_args.@"volatile",
- .output = output,
- .inputs = inputs,
- .clobbers = clobbers,
- .args = args,
- };
- try b.instructions.append(mod.gpa, &inst.base);
- return &inst.base;
-}
-
-fn analyzeInstCmp(
- mod: *Module,
- scope: *Scope,
- inst: *zir.Inst.BinOp,
- op: std.math.CompareOperator,
-) InnerError!*Inst {
- const lhs = try resolveInst(mod, scope, inst.positionals.lhs);
- const rhs = try resolveInst(mod, scope, inst.positionals.rhs);
-
- const is_equality_cmp = switch (op) {
- .eq, .neq => true,
- else => false,
- };
- const lhs_ty_tag = lhs.ty.zigTypeTag();
- const rhs_ty_tag = rhs.ty.zigTypeTag();
- if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) {
- // null == null, null != null
- return mod.constBool(scope, inst.base.src, op == .eq);
- } else if (is_equality_cmp and
- ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or
- rhs_ty_tag == .Null and lhs_ty_tag == .Optional))
- {
- // comparing null with optionals
- const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs;
- return mod.analyzeIsNull(scope, inst.base.src, opt_operand, op == .neq);
- } else if (is_equality_cmp and
- ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))
- {
- return mod.fail(scope, inst.base.src, "TODO implement C pointer cmp", .{});
- } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {
- const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty;
- return mod.fail(scope, inst.base.src, "comparison of '{}' with null", .{non_null_type});
- } else if (is_equality_cmp and
- ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or
- (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union)))
- {
- return mod.fail(scope, inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{});
- } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) {
- if (!is_equality_cmp) {
- return mod.fail(scope, inst.base.src, "{} operator not allowed for errors", .{@tagName(op)});
- }
- return mod.fail(scope, inst.base.src, "TODO implement equality comparison between errors", .{});
- } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) {
- // This operation allows any combination of integer and float types, regardless of the
- // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
- // numeric types.
- return mod.cmpNumeric(scope, inst.base.src, lhs, rhs, op);
- }
- return mod.fail(scope, inst.base.src, "TODO implement more cmp analysis", .{});
-}
-
-fn analyzeInstTypeOf(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- const operand = try resolveInst(mod, scope, inst.positionals.operand);
- return mod.constType(scope, inst.base.src, operand.ty);
-}
-
-fn analyzeInstBoolNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- const uncasted_operand = try resolveInst(mod, scope, inst.positionals.operand);
- const bool_type = Type.initTag(.bool);
- const operand = try mod.coerce(scope, bool_type, uncasted_operand);
- if (try mod.resolveDefinedValue(scope, operand)) |val| {
- return mod.constBool(scope, inst.base.src, !val.toBool());
- }
- const b = try mod.requireRuntimeBlock(scope, inst.base.src);
- return mod.addUnOp(b, inst.base.src, bool_type, .not, operand);
-}
-
-fn analyzeInstIsNonNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst {
- const operand = try resolveInst(mod, scope, inst.positionals.operand);
- return mod.analyzeIsNull(scope, inst.base.src, operand, invert_logic);
-}
-
-fn analyzeInstIsErr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- const operand = try resolveInst(mod, scope, inst.positionals.operand);
- return mod.analyzeIsErr(scope, inst.base.src, operand);
-}
-
-fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst {
- const uncasted_cond = try resolveInst(mod, scope, inst.positionals.condition);
- const cond = try mod.coerce(scope, Type.initTag(.bool), uncasted_cond);
-
- if (try mod.resolveDefinedValue(scope, cond)) |cond_val| {
- const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body;
- try analyzeBody(mod, scope, body.*);
- return mod.constVoid(scope, inst.base.src);
- }
-
- const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);
-
- var true_block: Scope.Block = .{
- .parent = parent_block,
- .func = parent_block.func,
- .decl = parent_block.decl,
- .instructions = .{},
- .arena = parent_block.arena,
- .is_comptime = parent_block.is_comptime,
- };
- defer true_block.instructions.deinit(mod.gpa);
- try analyzeBody(mod, &true_block.base, inst.positionals.then_body);
-
- var false_block: Scope.Block = .{
- .parent = parent_block,
- .func = parent_block.func,
- .decl = parent_block.decl,
- .instructions = .{},
- .arena = parent_block.arena,
- .is_comptime = parent_block.is_comptime,
- };
- defer false_block.instructions.deinit(mod.gpa);
- try analyzeBody(mod, &false_block.base, inst.positionals.else_body);
-
- const then_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) };
- const else_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) };
- return mod.addCondBr(parent_block, inst.base.src, cond, then_body, else_body);
-}
-
-fn analyzeInstUnreachable(
- mod: *Module,
- scope: *Scope,
- unreach: *zir.Inst.NoOp,
- safety_check: bool,
-) InnerError!*Inst {
- const b = try mod.requireRuntimeBlock(scope, unreach.base.src);
- // TODO Add compile error for @optimizeFor occurring too late in a scope.
- if (safety_check and mod.wantSafety(scope)) {
- return mod.safetyPanic(b, unreach.base.src, .unreach);
- } else {
- return mod.addNoOp(b, unreach.base.src, Type.initTag(.noreturn), .unreach);
- }
-}
-
-fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- const operand = try resolveInst(mod, scope, inst.positionals.operand);
- const b = try mod.requireRuntimeBlock(scope, inst.base.src);
- return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand);
-}
-
-fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
- const b = try mod.requireRuntimeBlock(scope, inst.base.src);
- if (b.func) |func| {
- // Need to emit a compile error if returning void is not allowed.
- const void_inst = try mod.constVoid(scope, inst.base.src);
- const fn_ty = func.owner_decl.typed_value.most_recent.typed_value.ty;
- const casted_void = try mod.coerce(scope, fn_ty.fnReturnType(), void_inst);
- if (casted_void.ty.zigTypeTag() != .Void) {
- return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, casted_void);
- }
- }
- return mod.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid);
-}
-
-fn floatOpAllowed(tag: zir.Inst.Tag) bool {
- // extend this swich as additional operators are implemented
- return switch (tag) {
- .add, .sub => true,
- else => false,
- };
-}
-
-fn analyzeBreak(
- mod: *Module,
- scope: *Scope,
- src: usize,
- zir_block: *zir.Inst.Block,
- operand: *Inst,
-) InnerError!*Inst {
- var opt_block = scope.cast(Scope.Block);
- while (opt_block) |block| {
- if (block.label) |*label| {
- if (label.zir_block == zir_block) {
- try label.results.append(mod.gpa, operand);
- const b = try mod.requireRuntimeBlock(scope, src);
- return mod.addBr(b, src, label.block_inst, operand);
- }
- }
- opt_block = block.parent;
- } else unreachable;
-}
-
-fn analyzeDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Decl {
- const decl_name = inst.positionals.name;
- const zir_module = scope.namespace().cast(Scope.ZIRModule).?;
- const src_decl = zir_module.contents.module.findDecl(decl_name) orelse
- return mod.fail(scope, inst.base.src, "use of undeclared identifier '{}'", .{decl_name});
-
- const decl = try resolveCompleteZirDecl(mod, scope, src_decl.decl);
-
- return decl;
-}
-
-fn analyzeInstSimplePtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, mutable: bool, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*Inst {
- const elem_type = try resolveType(mod, scope, inst.positionals.operand);
- const ty = try mod.simplePtrType(scope, inst.base.src, elem_type, mutable, size);
- return mod.constType(scope, inst.base.src, ty);
-}
-
-fn analyzeInstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.PtrType) InnerError!*Inst {
- // TODO lazy values
- const @"align" = if (inst.kw_args.@"align") |some|
- @truncate(u32, try resolveInt(mod, scope, some, Type.initTag(.u32)))
- else
- 0;
- const bit_offset = if (inst.kw_args.align_bit_start) |some|
- @truncate(u16, try resolveInt(mod, scope, some, Type.initTag(.u16)))
- else
- 0;
- const host_size = if (inst.kw_args.align_bit_end) |some|
- @truncate(u16, try resolveInt(mod, scope, some, Type.initTag(.u16)))
- else
- 0;
-
- if (host_size != 0 and bit_offset >= host_size * 8)
- return mod.fail(scope, inst.base.src, "bit offset starts after end of host integer", .{});
-
- const sentinel = if (inst.kw_args.sentinel) |some|
- (try resolveInstConst(mod, scope, some)).val
- else
- null;
-
- const elem_type = try resolveType(mod, scope, inst.positionals.child_type);
-
- const ty = try mod.ptrType(
- scope,
- inst.base.src,
- elem_type,
- sentinel,
- @"align",
- bit_offset,
- host_size,
- inst.kw_args.mutable,
- inst.kw_args.@"allowzero",
- inst.kw_args.@"volatile",
- inst.kw_args.size,
- );
- return mod.constType(scope, inst.base.src, ty);
-}
diff --git a/src/Cache.zig b/src/Cache.zig
new file mode 100644
index 0000000000000000000000000000000000000000..dff6f7e38eea2fc00da20c638eec90d640c7ce5c
--- /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 0000000000000000000000000000000000000000..623635a6b02d92c5bd048ff2d157e1782194e731
--- /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/DepTokenizer.zig b/src/DepTokenizer.zig
new file mode 100644
index 0000000000000000000000000000000000000000..cc2211a1aa1bb90ecbdc825b1633229886bbc408
--- /dev/null
+++ b/src/DepTokenizer.zig
@@ -0,0 +1,1064 @@
+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 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);
+ },
+ },
+ }
+ } else {
+ switch (self.state) {
+ .lhs,
+ .rhs,
+ .rhs_continuation,
+ .rhs_continuation_linefeed,
+ => return null,
+ .target => {
+ return errorPosition(.incomplete_target, start, self.bytes[start..]);
+ },
+ .target_reverse_solidus,
+ .target_dollar_sign,
+ => {
+ const idx = self.index - 1;
+ return errorIllegalChar(.incomplete_escape, idx, self.bytes[idx]);
+ },
+ .target_colon => {
+ const bytes = self.bytes[start .. self.index - 1];
+ if (bytes.len != 0) {
+ self.index += 1;
+ self.state = .rhs;
+ return finishTarget(must_resolve, bytes);
+ }
+ // silently ignore null target
+ self.state = .lhs;
+ return null;
+ },
+ .target_colon_reverse_solidus => {
+ const bytes = self.bytes[start .. self.index - 2];
+ if (bytes.len != 0) {
+ self.index += 1;
+ self.state = .rhs;
+ return finishTarget(must_resolve, bytes);
+ }
+ // silently ignore null target
+ self.state = .lhs;
+ return null;
+ },
+ .prereq_quote => {
+ return errorPosition(.incomplete_quoted_prerequisite, start, self.bytes[start..]);
+ },
+ .prereq => {
+ self.state = .lhs;
+ return Token{ .prereq = self.bytes[start..] };
+ },
+ .prereq_continuation => {
+ self.state = .lhs;
+ return Token{ .prereq = self.bytes[start .. self.index - 1] };
+ },
+ .prereq_continuation_linefeed => {
+ self.state = .lhs;
+ return Token{ .prereq = self.bytes[start .. self.index - 2] };
+ },
+ }
+ }
+ unreachable;
+}
+
+fn errorPosition(comptime id: @TagType(Token), index: usize, bytes: []const u8) Token {
+ return @unionInit(Token, @tagName(id), .{ .index = index, .bytes = bytes });
+}
+
+fn errorIllegalChar(comptime id: @TagType(Token), index: usize, char: u8) Token {
+ return @unionInit(Token, @tagName(id), .{ .index = index, .char = char });
+}
+
+fn finishTarget(must_resolve: bool, bytes: []const u8) Token {
+ return if (must_resolve)
+ .{ .target_must_resolve = bytes }
+ else
+ .{ .target = bytes };
+}
+
+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,
+ };
+
+ pub const IndexAndBytes = struct {
+ index: usize,
+ bytes: []const u8,
+ };
+
+ /// 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" {
+ try depTokenizer("", "");
+}
+
+test "empty whitespace" {
+ try depTokenizer("\n", "");
+ try depTokenizer("\r", "");
+ try depTokenizer("\r\n", "");
+ try depTokenizer(" ", "");
+}
+
+test "empty colon" {
+ try depTokenizer(":", "");
+ try depTokenizer("\n:", "");
+ try depTokenizer("\r:", "");
+ try depTokenizer("\r\n:", "");
+ try depTokenizer(" :", "");
+}
+
+test "empty target" {
+ try depTokenizer("foo.o:", "target = {foo.o}");
+ try depTokenizer(
+ \\foo.o:
+ \\bar.o:
+ \\abcd.o:
+ ,
+ \\target = {foo.o}
+ \\target = {bar.o}
+ \\target = {abcd.o}
+ );
+}
+
+test "whitespace empty target" {
+ try depTokenizer("\nfoo.o:", "target = {foo.o}");
+ try depTokenizer("\rfoo.o:", "target = {foo.o}");
+ try depTokenizer("\r\nfoo.o:", "target = {foo.o}");
+ try depTokenizer(" foo.o:", "target = {foo.o}");
+}
+
+test "escape empty target" {
+ try depTokenizer("\\ foo.o:", "target = { foo.o}");
+ try depTokenizer("\\#foo.o:", "target = {#foo.o}");
+ try depTokenizer("\\\\foo.o:", "target = {\\foo.o}");
+ try depTokenizer("$$foo.o:", "target = {$foo.o}");
+}
+
+test "empty target linefeeds" {
+ try depTokenizer("\n", "");
+ try depTokenizer("\r\n", "");
+
+ const expect = "target = {foo.o}";
+ try depTokenizer(
+ \\foo.o:
+ , expect);
+ try depTokenizer(
+ \\foo.o:
+ \\
+ , expect);
+ try depTokenizer(
+ \\foo.o:
+ , expect);
+ try depTokenizer(
+ \\foo.o:
+ \\
+ , expect);
+}
+
+test "empty target linefeeds + continuations" {
+ const expect = "target = {foo.o}";
+ try depTokenizer(
+ \\foo.o:\
+ , expect);
+ try depTokenizer(
+ \\foo.o:\
+ \\
+ , expect);
+ try depTokenizer(
+ \\foo.o:\
+ , expect);
+ try depTokenizer(
+ \\foo.o:\
+ \\
+ , expect);
+}
+
+test "empty target linefeeds + hspace + continuations" {
+ const expect = "target = {foo.o}";
+ try depTokenizer(
+ \\foo.o: \
+ , expect);
+ try depTokenizer(
+ \\foo.o: \
+ \\
+ , expect);
+ try depTokenizer(
+ \\foo.o: \
+ , expect);
+ try depTokenizer(
+ \\foo.o: \
+ \\
+ , expect);
+}
+
+test "prereq" {
+ const expect =
+ \\target = {foo.o}
+ \\prereq = {foo.c}
+ ;
+ try depTokenizer("foo.o: foo.c", expect);
+ try depTokenizer(
+ \\foo.o: \
+ \\foo.c
+ , expect);
+ try depTokenizer(
+ \\foo.o: \
+ \\ foo.c
+ , expect);
+ try depTokenizer(
+ \\foo.o: \
+ \\ foo.c
+ , expect);
+}
+
+test "prereq continuation" {
+ const expect =
+ \\target = {foo.o}
+ \\prereq = {foo.h}
+ \\prereq = {bar.h}
+ ;
+ try depTokenizer(
+ \\foo.o: foo.h\
+ \\bar.h
+ , expect);
+ try depTokenizer(
+ \\foo.o: foo.h\
+ \\bar.h
+ , expect);
+}
+
+test "multiple prereqs" {
+ const expect =
+ \\target = {foo.o}
+ \\prereq = {foo.c}
+ \\prereq = {foo.h}
+ \\prereq = {bar.h}
+ ;
+ try depTokenizer("foo.o: foo.c foo.h bar.h", expect);
+ try depTokenizer(
+ \\foo.o: \
+ \\foo.c foo.h bar.h
+ , expect);
+ try depTokenizer(
+ \\foo.o: foo.c foo.h bar.h\
+ , expect);
+ try depTokenizer(
+ \\foo.o: foo.c foo.h bar.h\
+ \\
+ , expect);
+ try depTokenizer(
+ \\foo.o: \
+ \\foo.c \
+ \\ foo.h\
+ \\bar.h
+ \\
+ , expect);
+ try depTokenizer(
+ \\foo.o: \
+ \\foo.c \
+ \\ foo.h\
+ \\bar.h\
+ \\
+ , expect);
+ try depTokenizer(
+ \\foo.o: \
+ \\foo.c \
+ \\ foo.h\
+ \\bar.h\
+ , expect);
+}
+
+test "multiple targets and prereqs" {
+ try depTokenizer(
+ \\foo.o: foo.c
+ \\bar.o: bar.c a.h b.h c.h
+ \\abc.o: abc.c \
+ \\ one.h two.h \
+ \\ three.h four.h
+ ,
+ \\target = {foo.o}
+ \\prereq = {foo.c}
+ \\target = {bar.o}
+ \\prereq = {bar.c}
+ \\prereq = {a.h}
+ \\prereq = {b.h}
+ \\prereq = {c.h}
+ \\target = {abc.o}
+ \\prereq = {abc.c}
+ \\prereq = {one.h}
+ \\prereq = {two.h}
+ \\prereq = {three.h}
+ \\prereq = {four.h}
+ );
+ try depTokenizer(
+ \\ascii.o: ascii.c
+ \\base64.o: base64.c stdio.h
+ \\elf.o: elf.c a.h b.h c.h
+ \\macho.o: \
+ \\ macho.c\
+ \\ a.h b.h c.h
+ ,
+ \\target = {ascii.o}
+ \\prereq = {ascii.c}
+ \\target = {base64.o}
+ \\prereq = {base64.c}
+ \\prereq = {stdio.h}
+ \\target = {elf.o}
+ \\prereq = {elf.c}
+ \\prereq = {a.h}
+ \\prereq = {b.h}
+ \\prereq = {c.h}
+ \\target = {macho.o}
+ \\prereq = {macho.c}
+ \\prereq = {a.h}
+ \\prereq = {b.h}
+ \\prereq = {c.h}
+ );
+ try depTokenizer(
+ \\a$$scii.o: ascii.c
+ \\\\base64.o: "\base64.c" "s t#dio.h"
+ \\e\\lf.o: "e\lf.c" "a.h$$" "$$b.h c.h$$"
+ \\macho.o: \
+ \\ "macho!.c" \
+ \\ a.h b.h c.h
+ ,
+ \\target = {a$scii.o}
+ \\prereq = {ascii.c}
+ \\target = {\base64.o}
+ \\prereq = {\base64.c}
+ \\prereq = {s t#dio.h}
+ \\target = {e\lf.o}
+ \\prereq = {e\lf.c}
+ \\prereq = {a.h$$}
+ \\prereq = {$$b.h c.h$$}
+ \\target = {macho.o}
+ \\prereq = {macho!.c}
+ \\prereq = {a.h}
+ \\prereq = {b.h}
+ \\prereq = {c.h}
+ );
+}
+
+test "windows quoted prereqs" {
+ try depTokenizer(
+ \\c:\foo.o: "C:\Program Files (x86)\Microsoft Visual Studio\foo.c"
+ \\c:\foo2.o: "C:\Program Files (x86)\Microsoft Visual Studio\foo2.c" \
+ \\ "C:\Program Files (x86)\Microsoft Visual Studio\foo1.h" \
+ \\ "C:\Program Files (x86)\Microsoft Visual Studio\foo2.h"
+ ,
+ \\target = {c:\foo.o}
+ \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo.c}
+ \\target = {c:\foo2.o}
+ \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo2.c}
+ \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo1.h}
+ \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo2.h}
+ );
+}
+
+test "windows mixed prereqs" {
+ try depTokenizer(
+ \\cimport.o: \
+ \\ C:\msys64\home\anon\project\zig\master\zig-cache\o\qhvhbUo7GU5iKyQ5mpA8TcQpncCYaQu0wwvr3ybiSTj_Dtqi1Nmcb70kfODJ2Qlg\cimport.h \
+ \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\stdio.h" \
+ \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt.h" \
+ \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime.h" \
+ \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\sal.h" \
+ \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\concurrencysal.h" \
+ \\ C:\msys64\opt\zig\lib\zig\include\vadefs.h \
+ \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vadefs.h" \
+ \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstdio.h" \
+ \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_stdio_config.h" \
+ \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\string.h" \
+ \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memory.h" \
+ \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memcpy_s.h" \
+ \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\errno.h" \
+ \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime_string.h" \
+ \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstring.h"
+ ,
+ \\target = {cimport.o}
+ \\prereq = {C:\msys64\home\anon\project\zig\master\zig-cache\o\qhvhbUo7GU5iKyQ5mpA8TcQpncCYaQu0wwvr3ybiSTj_Dtqi1Nmcb70kfODJ2Qlg\cimport.h}
+ \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\stdio.h}
+ \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt.h}
+ \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime.h}
+ \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\sal.h}
+ \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\concurrencysal.h}
+ \\prereq = {C:\msys64\opt\zig\lib\zig\include\vadefs.h}
+ \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vadefs.h}
+ \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstdio.h}
+ \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_stdio_config.h}
+ \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\string.h}
+ \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memory.h}
+ \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memcpy_s.h}
+ \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\errno.h}
+ \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime_string.h}
+ \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstring.h}
+ );
+}
+
+test "funky targets" {
+ try depTokenizer(
+ \\C:\Users\anon\foo.o:
+ \\C:\Users\anon\foo\ .o:
+ \\C:\Users\anon\foo\#.o:
+ \\C:\Users\anon\foo$$.o:
+ \\C:\Users\anon\\\ foo.o:
+ \\C:\Users\anon\\#foo.o:
+ \\C:\Users\anon\$$foo.o:
+ \\C:\Users\anon\\\ \ \ \ \ foo.o:
+ ,
+ \\target = {C:\Users\anon\foo.o}
+ \\target = {C:\Users\anon\foo .o}
+ \\target = {C:\Users\anon\foo#.o}
+ \\target = {C:\Users\anon\foo$.o}
+ \\target = {C:\Users\anon\ foo.o}
+ \\target = {C:\Users\anon\#foo.o}
+ \\target = {C:\Users\anon\$foo.o}
+ \\target = {C:\Users\anon\ foo.o}
+ );
+}
+
+test "error incomplete escape - reverse_solidus" {
+ try depTokenizer("\\",
+ \\ERROR: illegal char '\' at position 0: incomplete escape
+ );
+ try depTokenizer("\t\\",
+ \\ERROR: illegal char '\' at position 1: incomplete escape
+ );
+ try depTokenizer("\n\\",
+ \\ERROR: illegal char '\' at position 1: incomplete escape
+ );
+ try depTokenizer("\r\\",
+ \\ERROR: illegal char '\' at position 1: incomplete escape
+ );
+ try depTokenizer("\r\n\\",
+ \\ERROR: illegal char '\' at position 2: incomplete escape
+ );
+ try depTokenizer(" \\",
+ \\ERROR: illegal char '\' at position 1: incomplete escape
+ );
+}
+
+test "error incomplete escape - dollar_sign" {
+ try depTokenizer("$",
+ \\ERROR: illegal char '$' at position 0: incomplete escape
+ );
+ try depTokenizer("\t$",
+ \\ERROR: illegal char '$' at position 1: incomplete escape
+ );
+ try depTokenizer("\n$",
+ \\ERROR: illegal char '$' at position 1: incomplete escape
+ );
+ try depTokenizer("\r$",
+ \\ERROR: illegal char '$' at position 1: incomplete escape
+ );
+ try depTokenizer("\r\n$",
+ \\ERROR: illegal char '$' at position 2: incomplete escape
+ );
+ try depTokenizer(" $",
+ \\ERROR: illegal char '$' at position 1: incomplete escape
+ );
+}
+
+test "error incomplete target" {
+ try depTokenizer("foo.o",
+ \\ERROR: incomplete target 'foo.o' at position 0
+ );
+ try depTokenizer("\tfoo.o",
+ \\ERROR: incomplete target 'foo.o' at position 1
+ );
+ try depTokenizer("\nfoo.o",
+ \\ERROR: incomplete target 'foo.o' at position 1
+ );
+ try depTokenizer("\rfoo.o",
+ \\ERROR: incomplete target 'foo.o' at position 1
+ );
+ try depTokenizer("\r\nfoo.o",
+ \\ERROR: incomplete target 'foo.o' at position 2
+ );
+ try depTokenizer(" foo.o",
+ \\ERROR: incomplete target 'foo.o' at position 1
+ );
+
+ try depTokenizer("\\ foo.o",
+ \\ERROR: incomplete target ' foo.o' at position 0
+ );
+ try depTokenizer("\\#foo.o",
+ \\ERROR: incomplete target '#foo.o' at position 0
+ );
+ try depTokenizer("\\\\foo.o",
+ \\ERROR: incomplete target '\foo.o' at position 0
+ );
+ try depTokenizer("$$foo.o",
+ \\ERROR: incomplete target '$foo.o' at position 0
+ );
+}
+
+test "error illegal char at position - bad target escape" {
+ try depTokenizer("\\\t",
+ \\ERROR: illegal char \x09 at position 1: bad target escape
+ );
+ try depTokenizer("\\\n",
+ \\ERROR: illegal char \x0A at position 1: bad target escape
+ );
+ try depTokenizer("\\\r",
+ \\ERROR: illegal char \x0D at position 1: bad target escape
+ );
+ try depTokenizer("\\\r\n",
+ \\ERROR: illegal char \x0D at position 1: bad target escape
+ );
+}
+
+test "error illegal char at position - execting dollar_sign" {
+ try depTokenizer("$\t",
+ \\ERROR: illegal char \x09 at position 1: expecting '$'
+ );
+ try depTokenizer("$\n",
+ \\ERROR: illegal char \x0A at position 1: expecting '$'
+ );
+ try depTokenizer("$\r",
+ \\ERROR: illegal char \x0D at position 1: expecting '$'
+ );
+ try depTokenizer("$\r\n",
+ \\ERROR: illegal char \x0D at position 1: expecting '$'
+ );
+}
+
+test "error illegal char at position - invalid target" {
+ try depTokenizer("foo\t.o",
+ \\ERROR: illegal char \x09 at position 3: invalid target
+ );
+ try depTokenizer("foo\n.o",
+ \\ERROR: illegal char \x0A at position 3: invalid target
+ );
+ try depTokenizer("foo\r.o",
+ \\ERROR: illegal char \x0D at position 3: invalid target
+ );
+ try depTokenizer("foo\r\n.o",
+ \\ERROR: illegal char \x0D at position 3: invalid target
+ );
+}
+
+test "error target - continuation expecting end-of-line" {
+ try depTokenizer("foo.o: \\\t",
+ \\target = {foo.o}
+ \\ERROR: illegal char \x09 at position 8: continuation expecting end-of-line
+ );
+ try depTokenizer("foo.o: \\ ",
+ \\target = {foo.o}
+ \\ERROR: illegal char \x20 at position 8: continuation expecting end-of-line
+ );
+ try depTokenizer("foo.o: \\x",
+ \\target = {foo.o}
+ \\ERROR: illegal char 'x' at position 8: continuation expecting end-of-line
+ );
+ try depTokenizer("foo.o: \\\x0dx",
+ \\target = {foo.o}
+ \\ERROR: illegal char 'x' at position 9: continuation expecting end-of-line
+ );
+}
+
+test "error prereq - continuation expecting end-of-line" {
+ try depTokenizer("foo.o: foo.h\\\x0dx",
+ \\target = {foo.o}
+ \\ERROR: illegal char 'x' at position 14: 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.testing.allocator);
+ const arena = &arena_allocator.allocator;
+ defer arena_allocator.deinit();
+
+ 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 (it.next()) |token| {
+ if (i != 0) try buffer.appendSlice("\n");
+ 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;
+ },
+ }
+ i += 1;
+ }
+ const got: []const u8 = buffer.span();
+
+ if (std.mem.eql(u8, expect, got)) {
+ testing.expect(true);
+ return;
+ }
+
+ const out = std.io.getStdErr().writer();
+
+ 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);
+}
+
+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.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.writeAll(text);
+ var i: usize = text.len;
+ const end = 79;
+ while (i < 79) : (i += 1) {
+ try out.writeAll(&[_]u8{label[0]});
+ }
+ try out.writeAll("\n");
+}
+
+fn printRuler(out: anytype) !void {
+ var i: usize = 0;
+ const end = 79;
+ while (i < 79) : (i += 1) {
+ try out.writeAll("-");
+ }
+ try out.writeAll("\n");
+}
+
+fn hexDump(out: anytype, bytes: []const u8) !void {
+ const n16 = bytes.len >> 4;
+ var line: usize = 0;
+ var offset: usize = 0;
+ while (line < n16) : (line += 1) {
+ try hexDump16(out, offset, bytes[offset .. offset + 16]);
+ offset += 16;
+ }
+
+ const n = bytes.len & 0x0f;
+ if (n > 0) {
+ try printDecValue(out, offset, 8);
+ try out.writeAll(":");
+ try out.writeAll(" ");
+ var end1 = std.math.min(offset + n, offset + 8);
+ for (bytes[offset..end1]) |b| {
+ try out.writeAll(" ");
+ try printHexValue(out, b, 2);
+ }
+ var end2 = offset + n;
+ if (end2 > end1) {
+ try out.writeAll(" ");
+ for (bytes[end1..end2]) |b| {
+ try out.writeAll(" ");
+ try printHexValue(out, b, 2);
+ }
+ }
+ const short = 16 - n;
+ var i: usize = 0;
+ while (i < short) : (i += 1) {
+ try out.writeAll(" ");
+ }
+ if (end2 > end1) {
+ try out.writeAll(" |");
+ } else {
+ try out.writeAll(" |");
+ }
+ try printCharValues(out, bytes[offset..end2]);
+ try out.writeAll("|\n");
+ offset += n;
+ }
+
+ try printDecValue(out, offset, 8);
+ try out.writeAll(":");
+ try out.writeAll("\n");
+}
+
+fn hexDump16(out: anytype, offset: usize, bytes: []const u8) !void {
+ try printDecValue(out, offset, 8);
+ try out.writeAll(":");
+ try out.writeAll(" ");
+ for (bytes[0..8]) |b| {
+ try out.writeAll(" ");
+ try printHexValue(out, b, 2);
+ }
+ try out.writeAll(" ");
+ for (bytes[8..16]) |b| {
+ try out.writeAll(" ");
+ try printHexValue(out, b, 2);
+ }
+ try out.writeAll(" |");
+ try printCharValues(out, bytes);
+ 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 = 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 = width, .fill = '0' });
+ try out.writeAll(buffer[0..len]);
+}
+
+fn printCharValues(out: anytype, bytes: []const u8) !void {
+ for (bytes) |b| {
+ try out.writeAll(&[_]u8{printable_char_tab[b]});
+ }
+}
+
+fn printUnderstandableChar(out: anytype, char: u8) !void {
+ if (!std.ascii.isPrint(char) or char == ' ') {
+ try out.print("\\x{X:0>2}", .{char});
+ } else {
+ try out.print("'{c}'", .{printable_char_tab[char]});
+ }
+}
+
+// zig fmt: off
+const printable_char_tab: [256]u8 = (
+ "................................ !\"#$%&'()*+,-./0123456789:;<=>?" ++
+ "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~." ++
+ "................................................................" ++
+ "................................................................"
+).*;
+
diff --git a/src/Module.zig b/src/Module.zig
new file mode 100644
index 0000000000000000000000000000000000000000..4fcf72f4ff235af9373f146ed58819159a6761ad
--- /dev/null
+++ b/src/Module.zig
@@ -0,0 +1,3245 @@
+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;
+const Value = @import("value.zig").Value;
+const Type = @import("type.zig").Type;
+const TypedValue = @import("TypedValue.zig");
+const assert = std.debug.assert;
+const log = std.log.scoped(.module);
+const BigIntConst = std.math.big.int.Const;
+const BigIntMutable = std.math.big.int.Mutable;
+const Target = std.Target;
+const Package = @import("Package.zig");
+const link = @import("link.zig");
+const ir = @import("ir.zig");
+const zir = @import("zir.zig");
+const Inst = ir.Inst;
+const Body = ir.Body;
+const ast = std.zig.ast;
+const trace = @import("tracy.zig").trace;
+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,
+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,
+/// 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.
+decl_exports: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},
+/// We track which export is associated with the given symbol name for quick
+/// detection of symbol collisions.
+symbol_exports: std.StringArrayHashMapUnmanaged(*Export) = .{},
+/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
+/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
+/// is performing the export of another Decl.
+/// This table owns the Export memory.
+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) = .{},
+/// 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 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, *Compilation.ErrorMsg) = .{},
+/// Using a map here for consistency with the other fields here.
+/// 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 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,
+
+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,
+ /// Byte offset into the file that contains the export directive.
+ src: usize,
+ /// Represents the position of the export, if any, in the output file.
+ link: link.File.Elf.Export,
+ /// The Decl that performs the export. Note that this is *not* the Decl being exported.
+ owner_decl: *Decl,
+ /// The Decl being exported. Note this is *not* the Decl performing the export.
+ exported_decl: *Decl,
+ status: enum {
+ in_progress,
+ failed,
+ /// Indicates that the failure was due to a temporary issue, such as an I/O error
+ /// when writing to the output file. Retrying the export may succeed.
+ failed_retryable,
+ complete,
+ },
+};
+
+pub const Decl = struct {
+ /// This name is relative to the containing namespace of the decl. It uses a null-termination
+ /// to save bytes, since there can be a lot of decls in a compilation. The null byte is not allowed
+ /// in symbol names, because executable file formats use null-terminated strings for symbol names.
+ /// All Decls have names, even values that are not bound to a zig namespace. This is necessary for
+ /// mapping them to an address in the output file.
+ /// Memory owned by this decl, using Module's allocator.
+ name: [*:0]const u8,
+ /// The direct parent container of the Decl. This is either a `Scope.Container` or `Scope.ZIRModule`.
+ /// Reference to externally owned memory.
+ scope: *Scope,
+ /// The AST Node decl index or ZIR Inst index that contains this declaration.
+ /// Must be recomputed when the corresponding source file is modified.
+ src_index: usize,
+ /// The most recent value of the Decl after a successful semantic analysis.
+ typed_value: union(enum) {
+ never_succeeded: void,
+ most_recent: TypedValue.Managed,
+ },
+ /// Represents the "shallow" analysis status. For example, for decls that are functions,
+ /// the function type is analyzed with this set to `in_progress`, however, the semantic
+ /// analysis of the function body is performed with this value set to `success`. Functions
+ /// have their own analysis status field.
+ analysis: enum {
+ /// This Decl corresponds to an AST Node that has not been referenced yet, and therefore
+ /// because of Zig's lazy declaration analysis, it will remain unanalyzed until referenced.
+ unreferenced,
+ /// Semantic analysis for this Decl is running right now. This state detects dependency loops.
+ in_progress,
+ /// This Decl might be OK but it depends on another one which did not successfully complete
+ /// semantic analysis.
+ dependency_failure,
+ /// Semantic analysis failure.
+ /// There will be a corresponding ErrorMsg in Module.failed_decls.
+ sema_failure,
+ /// There will be a corresponding ErrorMsg in Module.failed_decls.
+ /// This indicates the failure was something like running out of disk space,
+ /// and attempting semantic analysis again may succeed.
+ sema_failure_retryable,
+ /// There will be a corresponding ErrorMsg in Module.failed_decls.
+ codegen_failure,
+ /// There will be a corresponding ErrorMsg in Module.failed_decls.
+ /// This indicates the failure was something like running out of disk space,
+ /// and attempting codegen again may succeed.
+ codegen_failure_retryable,
+ /// Everything is done. During an update, this Decl may be out of date, depending
+ /// on its dependencies. The `generation` field can be used to determine if this
+ /// completion status occurred before or after a given update.
+ complete,
+ /// A Module update is in progress, and this Decl has been flagged as being known
+ /// to require re-analysis.
+ outdated,
+ },
+ /// This flag is set when this Decl is added to a check_for_deletion set, and cleared
+ /// when removed.
+ deletion_flag: bool,
+ /// Whether the corresponding AST decl has a `pub` keyword.
+ is_pub: bool,
+
+ /// An integer that can be checked against the corresponding incrementing
+ /// generation field of Module. This is used to determine whether `complete` status
+ /// represents pre- or post- re-analysis.
+ generation: u32,
+
+ /// Represents the position of the code in the output file.
+ /// This is populated regardless of semantic analysis and code generation.
+ link: link.File.LinkBlock,
+
+ /// Represents the function in the linked output file, if the `Decl` is a function.
+ /// This is stored here and not in `Fn` because `Decl` survives across updates but
+ /// `Fn` does not.
+ /// TODO Look into making `Fn` a longer lived structure and moving this field there
+ /// to save on memory usage.
+ fn_link: link.File.LinkFn,
+
+ contents_hash: std.zig.SrcHash,
+
+ /// The shallow set of other decls whose typed_value could possibly change if this Decl's
+ /// typed_value is modified.
+ dependants: DepsTable = .{},
+ /// The shallow set of other decls whose typed_value changing indicates that this Decl's
+ /// typed_value may need to be regenerated.
+ dependencies: DepsTable = .{},
+
+ /// The reason this is not `std.AutoArrayHashMapUnmanaged` is a workaround for
+ /// stage1 compiler giving me: `error: struct 'Module.Decl' depends on itself`
+ pub const DepsTable = std.ArrayHashMapUnmanaged(*Decl, void, std.array_hash_map.getAutoHashFn(*Decl), std.array_hash_map.getAutoEqlFn(*Decl), false);
+
+ pub fn destroy(self: *Decl, gpa: *Allocator) void {
+ gpa.free(mem.spanZ(self.name));
+ if (self.typedValueManaged()) |tvm| {
+ tvm.deinit(gpa);
+ }
+ self.dependants.deinit(gpa);
+ self.dependencies.deinit(gpa);
+ gpa.destroy(self);
+ }
+
+ pub fn src(self: Decl) usize {
+ switch (self.scope.tag) {
+ .container => {
+ const container = @fieldParentPtr(Scope.Container, "base", self.scope);
+ const tree = container.file_scope.contents.tree;
+ // TODO Container should have it's own decls()
+ const decl_node = tree.root_node.decls()[self.src_index];
+ return tree.token_locs[decl_node.firstToken()].start;
+ },
+ .zir_module => {
+ const zir_module = @fieldParentPtr(Scope.ZIRModule, "base", self.scope);
+ const module = zir_module.contents.module;
+ const src_decl = module.decls[self.src_index];
+ return src_decl.inst.src;
+ },
+ .file, .block => unreachable,
+ .gen_zir => unreachable,
+ .local_val => unreachable,
+ .local_ptr => unreachable,
+ .decl => unreachable,
+ }
+ }
+
+ pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash {
+ return self.scope.fullyQualifiedNameHash(mem.spanZ(self.name));
+ }
+
+ pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue {
+ const tvm = self.typedValueManaged() orelse return error.AnalysisFail;
+ return tvm.typed_value;
+ }
+
+ pub fn value(self: *Decl) error{AnalysisFail}!Value {
+ return (try self.typedValue()).val;
+ }
+
+ pub fn dump(self: *Decl) void {
+ const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src);
+ std.debug.print("{}:{}:{} name={} status={}", .{
+ self.scope.sub_file_path,
+ loc.line + 1,
+ loc.column + 1,
+ mem.spanZ(self.name),
+ @tagName(self.analysis),
+ });
+ if (self.typedValueManaged()) |tvm| {
+ std.debug.print(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val });
+ }
+ std.debug.print("\n", .{});
+ }
+
+ pub fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {
+ switch (self.typed_value) {
+ .most_recent => |*x| return x,
+ .never_succeeded => return null,
+ }
+ }
+
+ fn removeDependant(self: *Decl, other: *Decl) void {
+ self.dependants.removeAssertDiscard(other);
+ }
+
+ fn removeDependency(self: *Decl, other: *Decl) void {
+ self.dependencies.removeAssertDiscard(other);
+ }
+};
+
+/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
+pub const Fn = struct {
+ /// This memory owned by the Decl's TypedValue.Managed arena allocator.
+ analysis: union(enum) {
+ queued: *ZIR,
+ in_progress,
+ /// There will be a corresponding ErrorMsg in Module.failed_decls
+ sema_failure,
+ /// This Fn might be OK but it depends on another Decl which did not successfully complete
+ /// semantic analysis.
+ dependency_failure,
+ success: Body,
+ },
+ owner_decl: *Decl,
+
+ /// This memory is temporary and points to stack memory for the duration
+ /// of Fn analysis.
+ pub const Analysis = struct {
+ inner_block: Scope.Block,
+ };
+
+ /// Contains un-analyzed ZIR instructions generated from Zig source AST.
+ pub const ZIR = struct {
+ body: zir.Module.Body,
+ arena: std.heap.ArenaAllocator.State,
+ };
+
+ /// For debugging purposes.
+ pub fn dump(self: *Fn, mod: Module) void {
+ std.debug.print("Module.Function(name={}) ", .{self.owner_decl.name});
+ switch (self.analysis) {
+ .queued => {
+ std.debug.print("queued\n", .{});
+ },
+ .in_progress => {
+ std.debug.print("in_progress\n", .{});
+ },
+ else => {
+ std.debug.print("\n", .{});
+ zir.dumpFn(mod, self);
+ },
+ }
+ }
+};
+
+pub const Var = struct {
+ /// if is_extern == true this is undefined
+ init: Value,
+ owner_decl: *Decl,
+
+ is_extern: bool,
+ is_mutable: bool,
+ is_threadlocal: bool,
+};
+
+pub const Scope = struct {
+ tag: Tag,
+
+ pub const NameHash = [16]u8;
+
+ pub fn cast(base: *Scope, comptime T: type) ?*T {
+ if (base.tag != T.base_tag)
+ return null;
+
+ return @fieldParentPtr(T, "base", base);
+ }
+
+ /// Asserts the scope has a parent which is a DeclAnalysis and
+ /// returns the arena Allocator.
+ pub fn arena(self: *Scope) *Allocator {
+ switch (self.tag) {
+ .block => return self.cast(Block).?.arena,
+ .decl => return &self.cast(DeclAnalysis).?.arena.allocator,
+ .gen_zir => return self.cast(GenZIR).?.arena,
+ .local_val => return self.cast(LocalVal).?.gen_zir.arena,
+ .local_ptr => return self.cast(LocalPtr).?.gen_zir.arena,
+ .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,
+ .file => unreachable,
+ .container => unreachable,
+ }
+ }
+
+ /// If the scope has a parent which is a `DeclAnalysis`,
+ /// returns the `Decl`, otherwise returns `null`.
+ pub fn decl(self: *Scope) ?*Decl {
+ return switch (self.tag) {
+ .block => self.cast(Block).?.decl,
+ .gen_zir => self.cast(GenZIR).?.decl,
+ .local_val => self.cast(LocalVal).?.gen_zir.decl,
+ .local_ptr => self.cast(LocalPtr).?.gen_zir.decl,
+ .decl => self.cast(DeclAnalysis).?.decl,
+ .zir_module => null,
+ .file => null,
+ .container => null,
+ };
+ }
+
+ /// Asserts the scope has a parent which is a ZIRModule or Container and
+ /// returns it.
+ pub fn namespace(self: *Scope) *Scope {
+ switch (self.tag) {
+ .block => return self.cast(Block).?.decl.scope,
+ .gen_zir => return self.cast(GenZIR).?.decl.scope,
+ .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope,
+ .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope,
+ .decl => return self.cast(DeclAnalysis).?.decl.scope,
+ .file => return &self.cast(File).?.root_container.base,
+ .zir_module, .container => return self,
+ }
+ }
+
+ /// Must generate unique bytes with no collisions with other decls.
+ /// The point of hashing here is only to limit the number of bytes of
+ /// the unique identifier to a fixed size (16 bytes).
+ pub fn fullyQualifiedNameHash(self: *Scope, name: []const u8) NameHash {
+ switch (self.tag) {
+ .block => unreachable,
+ .gen_zir => unreachable,
+ .local_val => unreachable,
+ .local_ptr => unreachable,
+ .decl => unreachable,
+ .file => unreachable,
+ .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name),
+ .container => return self.cast(Container).?.fullyQualifiedNameHash(name),
+ }
+ }
+
+ /// Asserts the scope is a child of a File and has an AST tree and returns the tree.
+ pub fn tree(self: *Scope) *ast.Tree {
+ switch (self.tag) {
+ .file => return self.cast(File).?.contents.tree,
+ .zir_module => unreachable,
+ .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(Container).?.file_scope.contents.tree,
+ .block => return self.cast(Block).?.decl.scope.cast(Container).?.file_scope.contents.tree,
+ .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(Container).?.file_scope.contents.tree,
+ .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree,
+ .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree,
+ .container => return self.cast(Container).?.file_scope.contents.tree,
+ }
+ }
+
+ /// Asserts the scope is a child of a `GenZIR` and returns it.
+ pub fn getGenZIR(self: *Scope) *GenZIR {
+ return switch (self.tag) {
+ .block => unreachable,
+ .gen_zir => self.cast(GenZIR).?,
+ .local_val => return self.cast(LocalVal).?.gen_zir,
+ .local_ptr => return self.cast(LocalPtr).?.gen_zir,
+ .decl => unreachable,
+ .zir_module => unreachable,
+ .file => unreachable,
+ .container => unreachable,
+ };
+ }
+
+ /// Asserts the scope has a parent which is a ZIRModule, Contaienr or File and
+ /// returns the sub_file_path field.
+ pub fn subFilePath(base: *Scope) []const u8 {
+ switch (base.tag) {
+ .container => return @fieldParentPtr(Container, "base", base).file_scope.sub_file_path,
+ .file => return @fieldParentPtr(File, "base", base).sub_file_path,
+ .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path,
+ .block => unreachable,
+ .gen_zir => unreachable,
+ .local_val => unreachable,
+ .local_ptr => unreachable,
+ .decl => unreachable,
+ }
+ }
+
+ pub fn unload(base: *Scope, gpa: *Allocator) void {
+ switch (base.tag) {
+ .file => return @fieldParentPtr(File, "base", base).unload(gpa),
+ .zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(gpa),
+ .block => unreachable,
+ .gen_zir => unreachable,
+ .local_val => unreachable,
+ .local_ptr => unreachable,
+ .decl => unreachable,
+ .container => unreachable,
+ }
+ }
+
+ pub fn getSource(base: *Scope, module: *Module) ![:0]const u8 {
+ switch (base.tag) {
+ .container => return @fieldParentPtr(Container, "base", base).file_scope.getSource(module),
+ .file => return @fieldParentPtr(File, "base", base).getSource(module),
+ .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module),
+ .gen_zir => unreachable,
+ .local_val => unreachable,
+ .local_ptr => unreachable,
+ .block => unreachable,
+ .decl => unreachable,
+ }
+ }
+
+ /// Asserts the scope is a namespace Scope and removes the Decl from the namespace.
+ pub fn removeDecl(base: *Scope, child: *Decl) void {
+ switch (base.tag) {
+ .container => return @fieldParentPtr(Container, "base", base).removeDecl(child),
+ .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child),
+ .file => unreachable,
+ .block => unreachable,
+ .gen_zir => unreachable,
+ .local_val => unreachable,
+ .local_ptr => unreachable,
+ .decl => unreachable,
+ }
+ }
+
+ /// Asserts the scope is a File or ZIRModule and deinitializes it, then deallocates it.
+ pub fn destroy(base: *Scope, gpa: *Allocator) void {
+ switch (base.tag) {
+ .file => {
+ const scope_file = @fieldParentPtr(File, "base", base);
+ scope_file.deinit(gpa);
+ gpa.destroy(scope_file);
+ },
+ .zir_module => {
+ const scope_zir_module = @fieldParentPtr(ZIRModule, "base", base);
+ scope_zir_module.deinit(gpa);
+ gpa.destroy(scope_zir_module);
+ },
+ .block => unreachable,
+ .gen_zir => unreachable,
+ .local_val => unreachable,
+ .local_ptr => unreachable,
+ .decl => unreachable,
+ .container => unreachable,
+ }
+ }
+
+ fn name_hash_hash(x: NameHash) u32 {
+ return @truncate(u32, @bitCast(u128, x));
+ }
+
+ fn name_hash_eql(a: NameHash, b: NameHash) bool {
+ return @bitCast(u128, a) == @bitCast(u128, b);
+ }
+
+ pub const Tag = enum {
+ /// .zir source code.
+ zir_module,
+ /// .zig source code.
+ file,
+ /// struct, enum or union, every .file contains one of these.
+ container,
+ block,
+ decl,
+ gen_zir,
+ local_val,
+ local_ptr,
+ };
+
+ pub const Container = struct {
+ pub const base_tag: Tag = .container;
+ base: Scope = Scope{ .tag = base_tag },
+
+ file_scope: *Scope.File,
+
+ /// Direct children of the file.
+ decls: std.AutoArrayHashMapUnmanaged(*Decl, void),
+
+ // TODO implement container types and put this in a status union
+ // ty: Type
+
+ pub fn deinit(self: *Container, gpa: *Allocator) void {
+ self.decls.deinit(gpa);
+ self.* = undefined;
+ }
+
+ pub fn removeDecl(self: *Container, child: *Decl) void {
+ _ = self.decls.remove(child);
+ }
+
+ pub fn fullyQualifiedNameHash(self: *Container, name: []const u8) NameHash {
+ // TODO container scope qualified names.
+ return std.zig.hashSrc(name);
+ }
+ };
+
+ pub const File = struct {
+ pub const base_tag: Tag = .file;
+ base: Scope = Scope{ .tag = base_tag },
+
+ /// Relative to the owning package's root_src_dir.
+ /// Reference to external memory, not owned by File.
+ sub_file_path: []const u8,
+ source: union(enum) {
+ unloaded: void,
+ bytes: [:0]const u8,
+ },
+ contents: union {
+ not_available: void,
+ tree: *ast.Tree,
+ },
+ status: enum {
+ never_loaded,
+ unloaded_success,
+ unloaded_parse_failure,
+ loaded_success,
+ },
+
+ root_container: Container,
+
+ pub fn unload(self: *File, gpa: *Allocator) void {
+ switch (self.status) {
+ .never_loaded,
+ .unloaded_parse_failure,
+ .unloaded_success,
+ => {},
+
+ .loaded_success => {
+ self.contents.tree.deinit();
+ self.status = .unloaded_success;
+ },
+ }
+ switch (self.source) {
+ .bytes => |bytes| {
+ gpa.free(bytes);
+ self.source = .{ .unloaded = {} };
+ },
+ .unloaded => {},
+ }
+ }
+
+ pub fn deinit(self: *File, gpa: *Allocator) void {
+ self.root_container.deinit(gpa);
+ self.unload(gpa);
+ self.* = undefined;
+ }
+
+ pub fn dumpSrc(self: *File, src: usize) void {
+ const loc = std.zig.findLineColumn(self.source.bytes, src);
+ std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
+ }
+
+ pub fn getSource(self: *File, module: *Module) ![:0]const u8 {
+ switch (self.source) {
+ .unloaded => {
+ const source = try module.root_pkg.root_src_directory.handle.readFileAllocOptions(
+ module.gpa,
+ self.sub_file_path,
+ std.math.maxInt(u32),
+ null,
+ 1,
+ 0,
+ );
+ self.source = .{ .bytes = source };
+ return source;
+ },
+ .bytes => |bytes| return bytes,
+ }
+ }
+ };
+
+ pub const ZIRModule = struct {
+ pub const base_tag: Tag = .zir_module;
+ base: Scope = Scope{ .tag = base_tag },
+ /// Relative to the owning package's root_src_dir.
+ /// Reference to external memory, not owned by ZIRModule.
+ sub_file_path: []const u8,
+ source: union(enum) {
+ unloaded: void,
+ bytes: [:0]const u8,
+ },
+ contents: union {
+ not_available: void,
+ module: *zir.Module,
+ },
+ status: enum {
+ never_loaded,
+ unloaded_success,
+ unloaded_parse_failure,
+ unloaded_sema_failure,
+
+ loaded_sema_failure,
+ loaded_success,
+ },
+
+ /// Even though .zir files only have 1 module, this set is still needed
+ /// because of anonymous Decls, which can exist in the global set, but
+ /// not this one.
+ decls: ArrayListUnmanaged(*Decl),
+
+ pub fn unload(self: *ZIRModule, gpa: *Allocator) void {
+ switch (self.status) {
+ .never_loaded,
+ .unloaded_parse_failure,
+ .unloaded_sema_failure,
+ .unloaded_success,
+ => {},
+
+ .loaded_success => {
+ self.contents.module.deinit(gpa);
+ gpa.destroy(self.contents.module);
+ self.contents = .{ .not_available = {} };
+ self.status = .unloaded_success;
+ },
+ .loaded_sema_failure => {
+ self.contents.module.deinit(gpa);
+ gpa.destroy(self.contents.module);
+ self.contents = .{ .not_available = {} };
+ self.status = .unloaded_sema_failure;
+ },
+ }
+ switch (self.source) {
+ .bytes => |bytes| {
+ gpa.free(bytes);
+ self.source = .{ .unloaded = {} };
+ },
+ .unloaded => {},
+ }
+ }
+
+ pub fn deinit(self: *ZIRModule, gpa: *Allocator) void {
+ self.decls.deinit(gpa);
+ self.unload(gpa);
+ self.* = undefined;
+ }
+
+ pub fn removeDecl(self: *ZIRModule, child: *Decl) void {
+ for (self.decls.items) |item, i| {
+ if (item == child) {
+ _ = self.decls.swapRemove(i);
+ return;
+ }
+ }
+ }
+
+ pub fn dumpSrc(self: *ZIRModule, src: usize) void {
+ const loc = std.zig.findLineColumn(self.source.bytes, src);
+ std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
+ }
+
+ pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 {
+ switch (self.source) {
+ .unloaded => {
+ const source = try module.root_pkg.root_src_directory.handle.readFileAllocOptions(
+ module.gpa,
+ self.sub_file_path,
+ std.math.maxInt(u32),
+ null,
+ 1,
+ 0,
+ );
+ self.source = .{ .bytes = source };
+ return source;
+ },
+ .bytes => |bytes| return bytes,
+ }
+ }
+
+ pub fn fullyQualifiedNameHash(self: *ZIRModule, name: []const u8) NameHash {
+ // ZIR modules only have 1 file with all decls global in the same namespace.
+ return std.zig.hashSrc(name);
+ }
+ };
+
+ /// This is a temporary structure, references to it are valid only
+ /// during semantic analysis of the block.
+ pub const Block = struct {
+ pub const base_tag: Tag = .block;
+ base: Scope = Scope{ .tag = base_tag },
+ parent: ?*Block,
+ func: ?*Fn,
+ decl: *Decl,
+ instructions: ArrayListUnmanaged(*Inst),
+ /// Points to the arena allocator of DeclAnalysis
+ arena: *Allocator,
+ label: ?Label = null,
+ is_comptime: bool,
+
+ pub const Label = struct {
+ zir_block: *zir.Inst.Block,
+ results: ArrayListUnmanaged(*Inst),
+ block_inst: *Inst.Block,
+ };
+ };
+
+ /// This is a temporary structure, references to it are valid only
+ /// during semantic analysis of the decl.
+ pub const DeclAnalysis = struct {
+ pub const base_tag: Tag = .decl;
+ base: Scope = Scope{ .tag = base_tag },
+ decl: *Decl,
+ arena: std.heap.ArenaAllocator,
+ };
+
+ /// This is a temporary structure, references to it are valid only
+ /// during semantic analysis of the decl.
+ pub const GenZIR = struct {
+ pub const base_tag: Tag = .gen_zir;
+ base: Scope = Scope{ .tag = base_tag },
+ /// Parents can be: `GenZIR`, `ZIRModule`, `File`
+ parent: *Scope,
+ decl: *Decl,
+ arena: *Allocator,
+ /// The first N instructions in a function body ZIR are arg instructions.
+ instructions: std.ArrayListUnmanaged(*zir.Inst) = .{},
+ label: ?Label = null,
+
+ pub const Label = struct {
+ token: ast.TokenIndex,
+ block_inst: *zir.Inst.Block,
+ result_loc: astgen.ResultLoc,
+ };
+ };
+
+ /// This is always a `const` local and importantly the `inst` is a value type, not a pointer.
+ /// This structure lives as long as the AST generation of the Block
+ /// node that contains the variable.
+ pub const LocalVal = struct {
+ pub const base_tag: Tag = .local_val;
+ base: Scope = Scope{ .tag = base_tag },
+ /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`.
+ parent: *Scope,
+ gen_zir: *GenZIR,
+ name: []const u8,
+ inst: *zir.Inst,
+ };
+
+ /// This could be a `const` or `var` local. It has a pointer instead of a value.
+ /// This structure lives as long as the AST generation of the Block
+ /// node that contains the variable.
+ pub const LocalPtr = struct {
+ pub const base_tag: Tag = .local_ptr;
+ base: Scope = Scope{ .tag = base_tag },
+ /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`.
+ parent: *Scope,
+ gen_zir: *GenZIR,
+ name: []const u8,
+ ptr: *zir.Inst,
+ };
+};
+
+pub const InnerError = error{ OutOfMemory, AnalysisFail };
+
+pub fn deinit(self: *Module) void {
+ const gpa = self.gpa;
+
+ self.zig_cache_artifact_directory.handle.close();
+
+ self.deletion_set.deinit(gpa);
+
+ for (self.decl_table.items()) |entry| {
+ entry.value.destroy(gpa);
+ }
+ self.decl_table.deinit(gpa);
+
+ for (self.failed_decls.items()) |entry| {
+ entry.value.destroy(gpa);
+ }
+ self.failed_decls.deinit(gpa);
+
+ for (self.failed_files.items()) |entry| {
+ entry.value.destroy(gpa);
+ }
+ self.failed_files.deinit(gpa);
+
+ for (self.failed_exports.items()) |entry| {
+ entry.value.destroy(gpa);
+ }
+ self.failed_exports.deinit(gpa);
+
+ for (self.decl_exports.items()) |entry| {
+ const export_list = entry.value;
+ gpa.free(export_list);
+ }
+ self.decl_exports.deinit(gpa);
+
+ for (self.export_owners.items()) |entry| {
+ freeExportList(gpa, entry.value);
+ }
+ self.export_owners.deinit(gpa);
+
+ self.symbol_exports.deinit(gpa);
+ self.root_scope.destroy(gpa);
+
+ var it = self.global_error_set.iterator();
+ while (it.next()) |entry| {
+ gpa.free(entry.key);
+ }
+ self.global_error_set.deinit(gpa);
+}
+
+fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
+ for (export_list) |exp| {
+ gpa.free(exp.options.name);
+ gpa.destroy(exp);
+ }
+ gpa.free(export_list);
+}
+
+pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const subsequent_analysis = switch (decl.analysis) {
+ .in_progress => unreachable,
+
+ .sema_failure,
+ .sema_failure_retryable,
+ .codegen_failure,
+ .dependency_failure,
+ .codegen_failure_retryable,
+ => return error.AnalysisFail,
+
+ .complete => return,
+
+ .outdated => blk: {
+ log.debug("re-analyzing {}\n", .{decl.name});
+
+ // The exports this Decl performs will be re-discovered, so we remove them here
+ // prior to re-analysis.
+ self.deleteDeclExports(decl);
+ // Dependencies will be re-discovered, so we remove them here prior to re-analysis.
+ for (decl.dependencies.items()) |entry| {
+ const dep = entry.key;
+ dep.removeDependant(decl);
+ if (dep.dependants.items().len == 0 and !dep.deletion_flag) {
+ // We don't perform a deletion here, because this Decl or another one
+ // may end up referencing it before the update is complete.
+ dep.deletion_flag = true;
+ try self.deletion_set.append(self.gpa, dep);
+ }
+ }
+ decl.dependencies.clearRetainingCapacity();
+
+ break :blk true;
+ },
+
+ .unreferenced => false,
+ };
+
+ const type_changed = if (self.root_scope.cast(Scope.ZIRModule)) |zir_module|
+ try zir_sema.analyzeZirDecl(self, decl, zir_module.contents.module.decls[decl.src_index])
+ else
+ self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ 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 Compilation.ErrorMsg.create(
+ self.gpa,
+ decl.src(),
+ "unable to analyze: {}",
+ .{@errorName(err)},
+ ));
+ decl.analysis = .sema_failure_retryable;
+ return error.AnalysisFail;
+ },
+ };
+
+ if (subsequent_analysis) {
+ // We may need to chase the dependants and re-analyze them.
+ // However, if the decl is a function, and the type is the same, we do not need to.
+ if (type_changed or decl.typed_value.most_recent.typed_value.val.tag() != .function) {
+ for (decl.dependants.items()) |entry| {
+ const dep = entry.key;
+ switch (dep.analysis) {
+ .unreferenced => unreachable,
+ .in_progress => unreachable,
+ .outdated => continue, // already queued for update
+
+ .dependency_failure,
+ .sema_failure,
+ .sema_failure_retryable,
+ .codegen_failure,
+ .codegen_failure_retryable,
+ .complete,
+ => if (dep.generation != self.generation) {
+ try self.markOutdatedDecl(dep);
+ },
+ }
+ }
+ }
+ }
+}
+
+fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const container_scope = decl.scope.cast(Scope.Container).?;
+ const tree = try self.getAstTree(container_scope);
+ const ast_node = tree.root_node.decls()[decl.src_index];
+ switch (ast_node.tag) {
+ .FnProto => {
+ const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", ast_node);
+
+ decl.analysis = .in_progress;
+
+ // This arena allocator's memory is discarded at the end of this function. It is used
+ // to determine the type of the function, and hence the type of the decl, which is needed
+ // to complete the Decl analysis.
+ var fn_type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
+ defer fn_type_scope_arena.deinit();
+ var fn_type_scope: Scope.GenZIR = .{
+ .decl = decl,
+ .arena = &fn_type_scope_arena.allocator,
+ .parent = decl.scope,
+ };
+ defer fn_type_scope.instructions.deinit(self.gpa);
+
+ decl.is_pub = fn_proto.getVisibToken() != null;
+ const body_node = fn_proto.getBodyNode() orelse
+ return self.failTok(&fn_type_scope.base, fn_proto.fn_token, "TODO implement extern functions", .{});
+
+ const param_decls = fn_proto.params();
+ const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len);
+
+ const fn_src = tree.token_locs[fn_proto.fn_token].start;
+ const type_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.type_type),
+ });
+ const type_type_rl: astgen.ResultLoc = .{ .ty = type_type };
+ for (param_decls) |param_decl, i| {
+ const param_type_node = switch (param_decl.param_type) {
+ .any_type => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement anytype parameter", .{}),
+ .type_expr => |node| node,
+ };
+ param_types[i] = try astgen.expr(self, &fn_type_scope.base, type_type_rl, param_type_node);
+ }
+ if (fn_proto.getVarArgsToken()) |var_args_token| {
+ return self.failTok(&fn_type_scope.base, var_args_token, "TODO implement var args", .{});
+ }
+ if (fn_proto.getLibName()) |lib_name| {
+ return self.failNode(&fn_type_scope.base, lib_name, "TODO implement function library name", .{});
+ }
+ if (fn_proto.getAlignExpr()) |align_expr| {
+ return self.failNode(&fn_type_scope.base, align_expr, "TODO implement function align expression", .{});
+ }
+ if (fn_proto.getSectionExpr()) |sect_expr| {
+ return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{});
+ }
+ if (fn_proto.getCallconvExpr()) |callconv_expr| {
+ return self.failNode(
+ &fn_type_scope.base,
+ callconv_expr,
+ "TODO implement function calling convention expression",
+ .{},
+ );
+ }
+ const return_type_expr = switch (fn_proto.return_type) {
+ .Explicit => |node| node,
+ .InferErrorSet => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement inferred error sets", .{}),
+ .Invalid => |tok| return self.failTok(&fn_type_scope.base, tok, "unable to parse return type", .{}),
+ };
+
+ const return_type_inst = try astgen.expr(self, &fn_type_scope.base, type_type_rl, return_type_expr);
+ const fn_type_inst = try astgen.addZIRInst(self, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{
+ .return_type = return_type_inst,
+ .param_types = param_types,
+ }, .{});
+
+ // We need the memory for the Type to go into the arena for the Decl
+ var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
+ errdefer decl_arena.deinit();
+ const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
+
+ var block_scope: Scope.Block = .{
+ .parent = null,
+ .func = null,
+ .decl = decl,
+ .instructions = .{},
+ .arena = &decl_arena.allocator,
+ .is_comptime = false,
+ };
+ defer block_scope.instructions.deinit(self.gpa);
+
+ const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{
+ .instructions = fn_type_scope.instructions.items,
+ });
+ const new_func = try decl_arena.allocator.create(Fn);
+ const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
+
+ const fn_zir = blk: {
+ // This scope's arena memory is discarded after the ZIR generation
+ // pass completes, and semantic analysis of it completes.
+ var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
+ errdefer gen_scope_arena.deinit();
+ var gen_scope: Scope.GenZIR = .{
+ .decl = decl,
+ .arena = &gen_scope_arena.allocator,
+ .parent = decl.scope,
+ };
+ defer gen_scope.instructions.deinit(self.gpa);
+
+ // We need an instruction for each parameter, and they must be first in the body.
+ try gen_scope.instructions.resize(self.gpa, fn_proto.params_len);
+ var params_scope = &gen_scope.base;
+ for (fn_proto.params()) |param, i| {
+ const name_token = param.name_token.?;
+ const src = tree.token_locs[name_token].start;
+ const param_name = tree.tokenSlice(name_token); // TODO: call identifierTokenString
+ const arg = try gen_scope_arena.allocator.create(zir.Inst.Arg);
+ arg.* = .{
+ .base = .{
+ .tag = .arg,
+ .src = src,
+ },
+ .positionals = .{
+ .name = param_name,
+ },
+ .kw_args = .{},
+ };
+ gen_scope.instructions.items[i] = &arg.base;
+ const sub_scope = try gen_scope_arena.allocator.create(Scope.LocalVal);
+ sub_scope.* = .{
+ .parent = params_scope,
+ .gen_zir = &gen_scope,
+ .name = param_name,
+ .inst = &arg.base,
+ };
+ params_scope = &sub_scope.base;
+ }
+
+ const body_block = body_node.cast(ast.Node.Block).?;
+
+ try astgen.blockExpr(self, params_scope, body_block);
+
+ if (gen_scope.instructions.items.len == 0 or
+ !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn())
+ {
+ const src = tree.token_locs[body_block.rbrace].start;
+ _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid);
+ }
+
+ const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR);
+ fn_zir.* = .{
+ .body = .{
+ .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items),
+ },
+ .arena = gen_scope_arena.state,
+ };
+ break :blk fn_zir;
+ };
+
+ new_func.* = .{
+ .analysis = .{ .queued = fn_zir },
+ .owner_decl = decl,
+ };
+ fn_payload.* = .{ .func = new_func };
+
+ var prev_type_has_bits = false;
+ var type_changed = true;
+
+ if (decl.typedValueManaged()) |tvm| {
+ prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
+ type_changed = !tvm.typed_value.ty.eql(fn_type);
+
+ tvm.deinit(self.gpa);
+ }
+
+ decl_arena_state.* = decl_arena.state;
+ decl.typed_value = .{
+ .most_recent = .{
+ .typed_value = .{
+ .ty = fn_type,
+ .val = Value.initPayload(&fn_payload.base),
+ },
+ .arena = decl_arena_state,
+ },
+ };
+ decl.analysis = .complete;
+ decl.generation = self.generation;
+
+ if (fn_type.hasCodeGenBits()) {
+ // 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.comp.bin_file.allocateDeclIndexes(decl);
+ try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });
+ } else if (prev_type_has_bits) {
+ self.comp.bin_file.freeDecl(decl);
+ }
+
+ if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
+ if (tree.token_ids[maybe_export_token] == .Keyword_export) {
+ const export_src = tree.token_locs[maybe_export_token].start;
+ const name_loc = tree.token_locs[fn_proto.getNameToken().?];
+ const name = tree.tokenSliceLoc(name_loc);
+ // The scope needs to have the decl in it.
+ try self.analyzeExport(&block_scope.base, export_src, name, decl);
+ }
+ }
+ return type_changed;
+ },
+ .VarDecl => {
+ const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node);
+
+ decl.analysis = .in_progress;
+
+ // We need the memory for the Type to go into the arena for the Decl
+ var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
+ errdefer decl_arena.deinit();
+ const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
+
+ var block_scope: Scope.Block = .{
+ .parent = null,
+ .func = null,
+ .decl = decl,
+ .instructions = .{},
+ .arena = &decl_arena.allocator,
+ .is_comptime = true,
+ };
+ defer block_scope.instructions.deinit(self.gpa);
+
+ decl.is_pub = var_decl.getVisibToken() != null;
+ const is_extern = blk: {
+ const maybe_extern_token = var_decl.getExternExportToken() orelse
+ break :blk false;
+ if (tree.token_ids[maybe_extern_token] != .Keyword_extern) break :blk false;
+ if (var_decl.getInitNode()) |some| {
+ return self.failNode(&block_scope.base, some, "extern variables have no initializers", .{});
+ }
+ break :blk true;
+ };
+ if (var_decl.getLibName()) |lib_name| {
+ assert(is_extern);
+ return self.failNode(&block_scope.base, lib_name, "TODO implement function library name", .{});
+ }
+ const is_mutable = tree.token_ids[var_decl.mut_token] == .Keyword_var;
+ const is_threadlocal = if (var_decl.getThreadLocalToken()) |some| blk: {
+ if (!is_mutable) {
+ return self.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{});
+ }
+ break :blk true;
+ } else false;
+ assert(var_decl.getComptimeToken() == null);
+ if (var_decl.getAlignNode()) |align_expr| {
+ return self.failNode(&block_scope.base, align_expr, "TODO implement function align expression", .{});
+ }
+ if (var_decl.getSectionNode()) |sect_expr| {
+ return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{});
+ }
+
+ const var_info: struct { ty: Type, val: ?Value } = if (var_decl.getInitNode()) |init_node| vi: {
+ var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
+ defer gen_scope_arena.deinit();
+ var gen_scope: Scope.GenZIR = .{
+ .decl = decl,
+ .arena = &gen_scope_arena.allocator,
+ .parent = decl.scope,
+ };
+ defer gen_scope.instructions.deinit(self.gpa);
+
+ const init_result_loc: astgen.ResultLoc = if (var_decl.getTypeNode()) |type_node| rl: {
+ const src = tree.token_locs[type_node.firstToken()].start;
+ const type_type = try astgen.addZIRInstConst(self, &gen_scope.base, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.type_type),
+ });
+ const var_type = try astgen.expr(self, &gen_scope.base, .{ .ty = type_type }, type_node);
+ break :rl .{ .ty = var_type };
+ } else .none;
+
+ const src = tree.token_locs[init_node.firstToken()].start;
+ const init_inst = try astgen.expr(self, &gen_scope.base, init_result_loc, init_node);
+
+ var inner_block: Scope.Block = .{
+ .parent = null,
+ .func = null,
+ .decl = decl,
+ .instructions = .{},
+ .arena = &gen_scope_arena.allocator,
+ .is_comptime = true,
+ };
+ defer inner_block.instructions.deinit(self.gpa);
+ try zir_sema.analyzeBody(self, &inner_block.base, .{ .instructions = gen_scope.instructions.items });
+
+ // The result location guarantees the type coercion.
+ const analyzed_init_inst = init_inst.analyzed_inst.?;
+ // The is_comptime in the Scope.Block guarantees the result is comptime-known.
+ const val = analyzed_init_inst.value().?;
+
+ const ty = try analyzed_init_inst.ty.copy(block_scope.arena);
+ break :vi .{
+ .ty = ty,
+ .val = try val.copy(block_scope.arena),
+ };
+ } else if (!is_extern) {
+ return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{});
+ } else if (var_decl.getTypeNode()) |type_node| vi: {
+ // Temporary arena for the zir instructions.
+ var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
+ defer type_scope_arena.deinit();
+ var type_scope: Scope.GenZIR = .{
+ .decl = decl,
+ .arena = &type_scope_arena.allocator,
+ .parent = decl.scope,
+ };
+ defer type_scope.instructions.deinit(self.gpa);
+
+ const src = tree.token_locs[type_node.firstToken()].start;
+ const type_type = try astgen.addZIRInstConst(self, &type_scope.base, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.type_type),
+ });
+ const var_type = try astgen.expr(self, &type_scope.base, .{ .ty = type_type }, type_node);
+ const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{
+ .instructions = type_scope.instructions.items,
+ });
+ break :vi .{
+ .ty = ty,
+ .val = null,
+ };
+ } else {
+ return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{});
+ };
+
+ if (is_mutable and !var_info.ty.isValidVarType(is_extern)) {
+ return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_info.ty});
+ }
+
+ var type_changed = true;
+ if (decl.typedValueManaged()) |tvm| {
+ type_changed = !tvm.typed_value.ty.eql(var_info.ty);
+
+ tvm.deinit(self.gpa);
+ }
+
+ const new_variable = try decl_arena.allocator.create(Var);
+ const var_payload = try decl_arena.allocator.create(Value.Payload.Variable);
+ new_variable.* = .{
+ .owner_decl = decl,
+ .init = var_info.val orelse undefined,
+ .is_extern = is_extern,
+ .is_mutable = is_mutable,
+ .is_threadlocal = is_threadlocal,
+ };
+ var_payload.* = .{ .variable = new_variable };
+
+ decl_arena_state.* = decl_arena.state;
+ decl.typed_value = .{
+ .most_recent = .{
+ .typed_value = .{
+ .ty = var_info.ty,
+ .val = Value.initPayload(&var_payload.base),
+ },
+ .arena = decl_arena_state,
+ },
+ };
+ decl.analysis = .complete;
+ decl.generation = self.generation;
+
+ if (var_decl.getExternExportToken()) |maybe_export_token| {
+ if (tree.token_ids[maybe_export_token] == .Keyword_export) {
+ const export_src = tree.token_locs[maybe_export_token].start;
+ const name_loc = tree.token_locs[var_decl.name_token];
+ const name = tree.tokenSliceLoc(name_loc);
+ // The scope needs to have the decl in it.
+ try self.analyzeExport(&block_scope.base, export_src, name, decl);
+ }
+ }
+ return type_changed;
+ },
+ .Comptime => {
+ const comptime_decl = @fieldParentPtr(ast.Node.Comptime, "base", ast_node);
+
+ decl.analysis = .in_progress;
+
+ // A comptime decl does not store any value so we can just deinit this arena after analysis is done.
+ var analysis_arena = std.heap.ArenaAllocator.init(self.gpa);
+ defer analysis_arena.deinit();
+ var gen_scope: Scope.GenZIR = .{
+ .decl = decl,
+ .arena = &analysis_arena.allocator,
+ .parent = decl.scope,
+ };
+ defer gen_scope.instructions.deinit(self.gpa);
+
+ _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr);
+
+ var block_scope: Scope.Block = .{
+ .parent = null,
+ .func = null,
+ .decl = decl,
+ .instructions = .{},
+ .arena = &analysis_arena.allocator,
+ .is_comptime = true,
+ };
+ defer block_scope.instructions.deinit(self.gpa);
+
+ _ = try zir_sema.analyzeBody(self, &block_scope.base, .{
+ .instructions = gen_scope.instructions.items,
+ });
+
+ decl.analysis = .complete;
+ decl.generation = self.generation;
+ return true;
+ },
+ .Use => @panic("TODO usingnamespace decl"),
+ else => unreachable,
+ }
+}
+
+fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {
+ try depender.dependencies.ensureCapacity(self.gpa, depender.dependencies.items().len + 1);
+ try dependee.dependants.ensureCapacity(self.gpa, dependee.dependants.items().len + 1);
+
+ depender.dependencies.putAssumeCapacity(dependee, {});
+ dependee.dependants.putAssumeCapacity(depender, {});
+}
+
+fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
+ switch (root_scope.status) {
+ .never_loaded, .unloaded_success => {
+ try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
+
+ const source = try root_scope.getSource(self);
+
+ var keep_zir_module = false;
+ const zir_module = try self.gpa.create(zir.Module);
+ defer if (!keep_zir_module) self.gpa.destroy(zir_module);
+
+ zir_module.* = try zir.parse(self.gpa, source);
+ defer if (!keep_zir_module) zir_module.deinit(self.gpa);
+
+ if (zir_module.error_msg) |src_err_msg| {
+ self.failed_files.putAssumeCapacityNoClobber(
+ &root_scope.base,
+ try Compilation.ErrorMsg.create(self.gpa, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}),
+ );
+ root_scope.status = .unloaded_parse_failure;
+ return error.AnalysisFail;
+ }
+
+ root_scope.status = .loaded_success;
+ root_scope.contents = .{ .module = zir_module };
+ keep_zir_module = true;
+
+ return zir_module;
+ },
+
+ .unloaded_parse_failure,
+ .unloaded_sema_failure,
+ => return error.AnalysisFail,
+
+ .loaded_success, .loaded_sema_failure => return root_scope.contents.module,
+ }
+}
+
+fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const root_scope = container_scope.file_scope;
+
+ switch (root_scope.status) {
+ .never_loaded, .unloaded_success => {
+ try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
+
+ const source = try root_scope.getSource(self);
+
+ var keep_tree = false;
+ const tree = try std.zig.parse(self.gpa, source);
+ defer if (!keep_tree) tree.deinit();
+
+ if (tree.errors.len != 0) {
+ const parse_err = tree.errors[0];
+
+ var msg = std.ArrayList(u8).init(self.gpa);
+ defer msg.deinit();
+
+ try parse_err.render(tree.token_ids, msg.outStream());
+ const err_msg = try self.gpa.create(Compilation.ErrorMsg);
+ err_msg.* = .{
+ .msg = msg.toOwnedSlice(),
+ .byte_offset = tree.token_locs[parse_err.loc()].start,
+ };
+
+ self.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg);
+ root_scope.status = .unloaded_parse_failure;
+ return error.AnalysisFail;
+ }
+
+ root_scope.status = .loaded_success;
+ root_scope.contents = .{ .tree = tree };
+ keep_tree = true;
+
+ return tree;
+ },
+
+ .unloaded_parse_failure => return error.AnalysisFail,
+
+ .loaded_success => return root_scope.contents.tree,
+ }
+}
+
+pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ // We may be analyzing it for the first time, or this may be
+ // an incremental update. This code handles both cases.
+ const tree = try self.getAstTree(container_scope);
+ const decls = tree.root_node.decls();
+
+ 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
+ // we know which ones have been deleted.
+ var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa);
+ defer deleted_decls.deinit();
+ try deleted_decls.ensureCapacity(container_scope.decls.items().len);
+ for (container_scope.decls.items()) |entry| {
+ deleted_decls.putAssumeCapacityNoClobber(entry.key, {});
+ }
+
+ for (decls) |src_decl, decl_i| {
+ if (src_decl.cast(ast.Node.FnProto)) |fn_proto| {
+ // We will create a Decl for it regardless of analysis status.
+ const name_tok = fn_proto.getNameToken() orelse {
+ @panic("TODO missing function name");
+ };
+
+ const name_loc = tree.token_locs[name_tok];
+ const name = tree.tokenSliceLoc(name_loc);
+ const name_hash = container_scope.fullyQualifiedNameHash(name);
+ const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
+ if (self.decl_table.get(name_hash)) |decl| {
+ // Update the AST Node index of the decl, even if its contents are unchanged, it may
+ // have been re-ordered.
+ decl.src_index = decl_i;
+ if (deleted_decls.remove(decl) == null) {
+ decl.analysis = .sema_failure;
+ 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.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.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
+ },
+ .macho => {
+ // TODO Implement for MachO
+ },
+ .c, .wasm => {},
+ }
+ }
+ } else {
+ const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
+ container_scope.decls.putAssumeCapacity(new_decl, {});
+ if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
+ if (tree.token_ids[maybe_export_token] == .Keyword_export) {
+ self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
+ }
+ }
+ }
+ } else if (src_decl.castTag(.VarDecl)) |var_decl| {
+ const name_loc = tree.token_locs[var_decl.name_token];
+ const name = tree.tokenSliceLoc(name_loc);
+ const name_hash = container_scope.fullyQualifiedNameHash(name);
+ const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
+ if (self.decl_table.get(name_hash)) |decl| {
+ // Update the AST Node index of the decl, even if its contents are unchanged, it may
+ // have been re-ordered.
+ decl.src_index = decl_i;
+ if (deleted_decls.remove(decl) == null) {
+ decl.analysis = .sema_failure;
+ 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)) {
+ try self.markOutdatedDecl(decl);
+ decl.contents_hash = contents_hash;
+ }
+ } else {
+ const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
+ container_scope.decls.putAssumeCapacity(new_decl, {});
+ if (var_decl.getExternExportToken()) |maybe_export_token| {
+ if (tree.token_ids[maybe_export_token] == .Keyword_export) {
+ self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
+ }
+ }
+ }
+ } else if (src_decl.castTag(.Comptime)) |comptime_node| {
+ const name_index = self.getNextAnonNameIndex();
+ const name = try std.fmt.allocPrint(self.gpa, "__comptime_{}", .{name_index});
+ defer self.gpa.free(name);
+
+ const name_hash = container_scope.fullyQualifiedNameHash(name);
+ const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
+
+ const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
+ container_scope.decls.putAssumeCapacity(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| {
+ log.err("TODO: analyze test decl", .{});
+ } else if (src_decl.castTag(.Use)) |use_decl| {
+ log.err("TODO: analyze usingnamespace decl", .{});
+ } else {
+ unreachable;
+ }
+ }
+ // Handle explicitly deleted decls from the source code. Not to be confused
+ // with when we delete decls because they are no longer referenced.
+ for (deleted_decls.items()) |entry| {
+ log.debug("noticed '{}' deleted from source\n", .{entry.key.name});
+ try self.deleteDecl(entry.key);
+ }
+}
+
+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.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);
+ defer exports_to_resolve.deinit();
+
+ // Keep track of the decls that we expect to see in this file so that
+ // we know which ones have been deleted.
+ var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa);
+ defer deleted_decls.deinit();
+ try deleted_decls.ensureCapacity(self.decl_table.items().len);
+ for (self.decl_table.items()) |entry| {
+ deleted_decls.putAssumeCapacityNoClobber(entry.value, {});
+ }
+
+ for (src_module.decls) |src_decl, decl_i| {
+ const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name);
+ if (self.decl_table.get(name_hash)) |decl| {
+ deleted_decls.removeAssertDiscard(decl);
+ if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) {
+ try self.markOutdatedDecl(decl);
+ decl.contents_hash = src_decl.contents_hash;
+ }
+ } else {
+ const new_decl = try self.createNewDecl(
+ &root_scope.base,
+ src_decl.name,
+ decl_i,
+ name_hash,
+ src_decl.contents_hash,
+ );
+ root_scope.decls.appendAssumeCapacity(new_decl);
+ if (src_decl.inst.cast(zir.Inst.Export)) |export_inst| {
+ try exports_to_resolve.append(src_decl);
+ }
+ }
+ }
+ for (exports_to_resolve.items) |export_decl| {
+ _ = try zir_sema.resolveZirDecl(self, &root_scope.base, export_decl);
+ }
+ // Handle explicitly deleted decls from the source code. Not to be confused
+ // with when we delete decls because they are no longer referenced.
+ for (deleted_decls.items()) |entry| {
+ log.debug("noticed '{}' deleted from source\n", .{entry.key.name});
+ try self.deleteDecl(entry.key);
+ }
+}
+
+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
+ // not be present in the set, and this does nothing.
+ decl.scope.removeDecl(decl);
+
+ log.debug("deleting decl '{}'\n", .{decl.name});
+ const name_hash = decl.fullyQualifiedNameHash();
+ self.decl_table.removeAssertDiscard(name_hash);
+ // Remove itself from its dependencies, because we are about to destroy the decl pointer.
+ for (decl.dependencies.items()) |entry| {
+ const dep = entry.key;
+ dep.removeDependant(decl);
+ if (dep.dependants.items().len == 0 and !dep.deletion_flag) {
+ // We don't recursively perform a deletion here, because during the update,
+ // another reference to it may turn up.
+ dep.deletion_flag = true;
+ self.deletion_set.appendAssumeCapacity(dep);
+ }
+ }
+ // Anything that depends on this deleted decl certainly needs to be re-analyzed.
+ for (decl.dependants.items()) |entry| {
+ const dep = entry.key;
+ dep.removeDependency(decl);
+ if (dep.analysis != .outdated) {
+ // TODO Move this failure possibility to the top of the function.
+ try self.markOutdatedDecl(dep);
+ }
+ }
+ if (self.failed_decls.remove(decl)) |entry| {
+ entry.value.destroy(self.gpa);
+ }
+ self.deleteDeclExports(decl);
+ self.comp.bin_file.freeDecl(decl);
+ decl.destroy(self.gpa);
+}
+
+/// Delete all the Export objects that are caused by this Decl. Re-analysis of
+/// this Decl will cause them to be re-created (or not).
+fn deleteDeclExports(self: *Module, decl: *Decl) void {
+ const kv = self.export_owners.remove(decl) orelse return;
+
+ for (kv.value) |exp| {
+ if (self.decl_exports.getEntry(exp.exported_decl)) |decl_exports_kv| {
+ // Remove exports with owner_decl matching the regenerating decl.
+ const list = decl_exports_kv.value;
+ var i: usize = 0;
+ var new_len = list.len;
+ while (i < new_len) {
+ if (list[i].owner_decl == decl) {
+ mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]);
+ new_len -= 1;
+ } else {
+ i += 1;
+ }
+ }
+ decl_exports_kv.value = self.gpa.shrink(list, new_len);
+ if (new_len == 0) {
+ self.decl_exports.removeAssertDiscard(exp.exported_decl);
+ }
+ }
+ if (self.comp.bin_file.cast(link.File.Elf)) |elf| {
+ elf.deleteExport(exp.link);
+ }
+ if (self.failed_exports.remove(exp)) |entry| {
+ entry.value.destroy(self.gpa);
+ }
+ _ = self.symbol_exports.remove(exp.options.name);
+ self.gpa.free(exp.options.name);
+ self.gpa.destroy(exp);
+ }
+ self.gpa.free(kv.value);
+}
+
+pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ // Use the Decl's arena for function memory.
+ var arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);
+ defer decl.typed_value.most_recent.arena.?.* = arena.state;
+ var inner_block: Scope.Block = .{
+ .parent = null,
+ .func = func,
+ .decl = decl,
+ .instructions = .{},
+ .arena = &arena.allocator,
+ .is_comptime = false,
+ };
+ defer inner_block.instructions.deinit(self.gpa);
+
+ const fn_zir = func.analysis.queued;
+ defer fn_zir.arena.promote(self.gpa).deinit();
+ func.analysis = .{ .in_progress = {} };
+ log.debug("set {} to in_progress\n", .{decl.name});
+
+ try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body);
+
+ const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
+ func.analysis = .{ .success = .{ .instructions = instructions } };
+ log.debug("set {} to success\n", .{decl.name});
+}
+
+fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
+ log.debug("mark {} outdated\n", .{decl.name});
+ try self.comp.work_queue.writeItem(.{ .analyze_decl = decl });
+ if (self.failed_decls.remove(decl)) |entry| {
+ entry.value.destroy(self.gpa);
+ }
+ decl.analysis = .outdated;
+}
+
+fn allocateNewDecl(
+ self: *Module,
+ scope: *Scope,
+ src_index: usize,
+ contents_hash: std.zig.SrcHash,
+) !*Decl {
+ const new_decl = try self.gpa.create(Decl);
+ new_decl.* = .{
+ .name = "",
+ .scope = scope.namespace(),
+ .src_index = src_index,
+ .typed_value = .{ .never_succeeded = {} },
+ .analysis = .unreferenced,
+ .deletion_flag = false,
+ .contents_hash = contents_hash,
+ .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.comp.bin_file.tag) {
+ .coff => .{ .coff = {} },
+ .elf => .{ .elf = link.File.Elf.SrcFn.empty },
+ .macho => .{ .macho = link.File.MachO.SrcFn.empty },
+ .c => .{ .c = {} },
+ .wasm => .{ .wasm = null },
+ },
+ .generation = 0,
+ .is_pub = false,
+ };
+ return new_decl;
+}
+
+fn createNewDecl(
+ self: *Module,
+ scope: *Scope,
+ decl_name: []const u8,
+ src_index: usize,
+ name_hash: Scope.NameHash,
+ contents_hash: std.zig.SrcHash,
+) !*Decl {
+ try self.decl_table.ensureCapacity(self.gpa, self.decl_table.items().len + 1);
+ const new_decl = try self.allocateNewDecl(scope, src_index, contents_hash);
+ errdefer self.gpa.destroy(new_decl);
+ new_decl.name = try mem.dupeZ(self.gpa, u8, decl_name);
+ self.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl);
+ return new_decl;
+}
+
+/// Get error value for error tag `name`.
+pub fn getErrorValue(self: *Module, name: []const u8) !std.StringHashMapUnmanaged(u16).Entry {
+ const gop = try self.global_error_set.getOrPut(self.gpa, name);
+ if (gop.found_existing)
+ return gop.entry.*;
+ errdefer self.global_error_set.removeAssertDiscard(name);
+
+ gop.entry.key = try self.gpa.dupe(u8, name);
+ gop.entry.value = @intCast(u16, self.global_error_set.count() - 1);
+ return gop.entry.*;
+}
+
+pub fn requireFunctionBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
+ return scope.cast(Scope.Block) orelse
+ return self.fail(scope, src, "instruction illegal outside function body", .{});
+}
+
+pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
+ const block = try self.requireFunctionBlock(scope, src);
+ if (block.is_comptime) {
+ return self.fail(scope, src, "unable to resolve comptime value", .{});
+ }
+ return block;
+}
+
+pub fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value {
+ return (try self.resolveDefinedValue(scope, base)) orelse
+ return self.fail(scope, base.src, "unable to resolve comptime value", .{});
+}
+
+pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
+ if (base.value()) |val| {
+ if (val.isUndef()) {
+ return self.fail(scope, base.src, "use of undefined value here causes undefined behavior", .{});
+ }
+ return val;
+ }
+ return null;
+}
+
+pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_name: []const u8, exported_decl: *Decl) !void {
+ try self.ensureDeclAnalyzed(exported_decl);
+ const typed_value = exported_decl.typed_value.most_recent.typed_value;
+ switch (typed_value.ty.zigTypeTag()) {
+ .Fn => {},
+ else => return self.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}),
+ }
+
+ try self.decl_exports.ensureCapacity(self.gpa, self.decl_exports.items().len + 1);
+ try self.export_owners.ensureCapacity(self.gpa, self.export_owners.items().len + 1);
+
+ const new_export = try self.gpa.create(Export);
+ errdefer self.gpa.destroy(new_export);
+
+ const symbol_name = try self.gpa.dupe(u8, borrowed_symbol_name);
+ errdefer self.gpa.free(symbol_name);
+
+ const owner_decl = scope.decl().?;
+
+ new_export.* = .{
+ .options = .{ .name = symbol_name },
+ .src = src,
+ .link = .{},
+ .owner_decl = owner_decl,
+ .exported_decl = exported_decl,
+ .status = .in_progress,
+ };
+
+ // Add to export_owners table.
+ const eo_gop = self.export_owners.getOrPutAssumeCapacity(owner_decl);
+ if (!eo_gop.found_existing) {
+ eo_gop.entry.value = &[0]*Export{};
+ }
+ eo_gop.entry.value = try self.gpa.realloc(eo_gop.entry.value, eo_gop.entry.value.len + 1);
+ eo_gop.entry.value[eo_gop.entry.value.len - 1] = new_export;
+ errdefer eo_gop.entry.value = self.gpa.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1);
+
+ // Add to exported_decl table.
+ const de_gop = self.decl_exports.getOrPutAssumeCapacity(exported_decl);
+ if (!de_gop.found_existing) {
+ de_gop.entry.value = &[0]*Export{};
+ }
+ de_gop.entry.value = try self.gpa.realloc(de_gop.entry.value, de_gop.entry.value.len + 1);
+ de_gop.entry.value[de_gop.entry.value.len - 1] = new_export;
+ errdefer de_gop.entry.value = self.gpa.shrink(de_gop.entry.value, de_gop.entry.value.len - 1);
+
+ 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 Compilation.ErrorMsg.create(
+ self.gpa,
+ src,
+ "exported symbol collision: {}",
+ .{symbol_name},
+ ));
+ // TODO: add a note
+ new_export.status = .failed;
+ return;
+ }
+
+ try self.symbol_exports.putNoClobber(self.gpa, symbol_name, new_export);
+ 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 Compilation.ErrorMsg.create(
+ self.gpa,
+ src,
+ "unable to export: {}",
+ .{@errorName(err)},
+ ));
+ new_export.status = .failed_retryable;
+ },
+ };
+}
+
+pub fn addNoOp(
+ self: *Module,
+ block: *Scope.Block,
+ src: usize,
+ ty: Type,
+ comptime tag: Inst.Tag,
+) !*Inst {
+ const inst = try block.arena.create(tag.Type());
+ inst.* = .{
+ .base = .{
+ .tag = tag,
+ .ty = ty,
+ .src = src,
+ },
+ };
+ try block.instructions.append(self.gpa, &inst.base);
+ return &inst.base;
+}
+
+pub fn addUnOp(
+ self: *Module,
+ block: *Scope.Block,
+ src: usize,
+ ty: Type,
+ tag: Inst.Tag,
+ operand: *Inst,
+) !*Inst {
+ const inst = try block.arena.create(Inst.UnOp);
+ inst.* = .{
+ .base = .{
+ .tag = tag,
+ .ty = ty,
+ .src = src,
+ },
+ .operand = operand,
+ };
+ try block.instructions.append(self.gpa, &inst.base);
+ return &inst.base;
+}
+
+pub fn addBinOp(
+ self: *Module,
+ block: *Scope.Block,
+ src: usize,
+ ty: Type,
+ tag: Inst.Tag,
+ lhs: *Inst,
+ rhs: *Inst,
+) !*Inst {
+ const inst = try block.arena.create(Inst.BinOp);
+ inst.* = .{
+ .base = .{
+ .tag = tag,
+ .ty = ty,
+ .src = src,
+ },
+ .lhs = lhs,
+ .rhs = rhs,
+ };
+ try block.instructions.append(self.gpa, &inst.base);
+ return &inst.base;
+}
+
+pub fn addArg(self: *Module, block: *Scope.Block, src: usize, ty: Type, name: [*:0]const u8) !*Inst {
+ const inst = try block.arena.create(Inst.Arg);
+ inst.* = .{
+ .base = .{
+ .tag = .arg,
+ .ty = ty,
+ .src = src,
+ },
+ .name = name,
+ };
+ try block.instructions.append(self.gpa, &inst.base);
+ return &inst.base;
+}
+
+pub fn addBr(
+ self: *Module,
+ scope_block: *Scope.Block,
+ src: usize,
+ target_block: *Inst.Block,
+ operand: *Inst,
+) !*Inst {
+ const inst = try scope_block.arena.create(Inst.Br);
+ inst.* = .{
+ .base = .{
+ .tag = .br,
+ .ty = Type.initTag(.noreturn),
+ .src = src,
+ },
+ .operand = operand,
+ .block = target_block,
+ };
+ try scope_block.instructions.append(self.gpa, &inst.base);
+ return &inst.base;
+}
+
+pub fn addCondBr(
+ self: *Module,
+ block: *Scope.Block,
+ src: usize,
+ condition: *Inst,
+ then_body: ir.Body,
+ else_body: ir.Body,
+) !*Inst {
+ const inst = try block.arena.create(Inst.CondBr);
+ inst.* = .{
+ .base = .{
+ .tag = .condbr,
+ .ty = Type.initTag(.noreturn),
+ .src = src,
+ },
+ .condition = condition,
+ .then_body = then_body,
+ .else_body = else_body,
+ };
+ try block.instructions.append(self.gpa, &inst.base);
+ return &inst.base;
+}
+
+pub fn addCall(
+ self: *Module,
+ block: *Scope.Block,
+ src: usize,
+ ty: Type,
+ func: *Inst,
+ args: []const *Inst,
+) !*Inst {
+ const inst = try block.arena.create(Inst.Call);
+ inst.* = .{
+ .base = .{
+ .tag = .call,
+ .ty = ty,
+ .src = src,
+ },
+ .func = func,
+ .args = args,
+ };
+ try block.instructions.append(self.gpa, &inst.base);
+ return &inst.base;
+}
+
+pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst {
+ const const_inst = try scope.arena().create(Inst.Constant);
+ const_inst.* = .{
+ .base = .{
+ .tag = Inst.Constant.base_tag,
+ .ty = typed_value.ty,
+ .src = src,
+ },
+ .val = typed_value.val,
+ };
+ return &const_inst.base;
+}
+
+pub fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
+ return self.constInst(scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = try ty.toValue(scope.arena()),
+ });
+}
+
+pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {
+ return self.constInst(scope, src, .{
+ .ty = Type.initTag(.void),
+ .val = Value.initTag(.void_value),
+ });
+}
+
+pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst {
+ return self.constInst(scope, src, .{
+ .ty = Type.initTag(.noreturn),
+ .val = Value.initTag(.unreachable_value),
+ });
+}
+
+pub fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
+ return self.constInst(scope, src, .{
+ .ty = ty,
+ .val = Value.initTag(.undef),
+ });
+}
+
+pub fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst {
+ return self.constInst(scope, src, .{
+ .ty = Type.initTag(.bool),
+ .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)],
+ });
+}
+
+pub fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64) !*Inst {
+ const int_payload = try scope.arena().create(Value.Payload.Int_u64);
+ int_payload.* = .{ .int = int };
+
+ return self.constInst(scope, src, .{
+ .ty = ty,
+ .val = Value.initPayload(&int_payload.base),
+ });
+}
+
+pub fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) !*Inst {
+ const int_payload = try scope.arena().create(Value.Payload.Int_i64);
+ int_payload.* = .{ .int = int };
+
+ return self.constInst(scope, src, .{
+ .ty = ty,
+ .val = Value.initPayload(&int_payload.base),
+ });
+}
+
+pub fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigIntConst) !*Inst {
+ const val_payload = if (big_int.positive) blk: {
+ if (big_int.to(u64)) |x| {
+ return self.constIntUnsigned(scope, src, ty, x);
+ } else |err| switch (err) {
+ error.NegativeIntoUnsigned => unreachable,
+ error.TargetTooSmall => {}, // handled below
+ }
+ const big_int_payload = try scope.arena().create(Value.Payload.IntBigPositive);
+ big_int_payload.* = .{ .limbs = big_int.limbs };
+ break :blk &big_int_payload.base;
+ } else blk: {
+ if (big_int.to(i64)) |x| {
+ return self.constIntSigned(scope, src, ty, x);
+ } else |err| switch (err) {
+ error.NegativeIntoUnsigned => unreachable,
+ error.TargetTooSmall => {}, // handled below
+ }
+ const big_int_payload = try scope.arena().create(Value.Payload.IntBigNegative);
+ big_int_payload.* = .{ .limbs = big_int.limbs };
+ break :blk &big_int_payload.base;
+ };
+
+ return self.constInst(scope, src, .{
+ .ty = ty,
+ .val = Value.initPayload(val_payload),
+ });
+}
+
+pub fn createAnonymousDecl(
+ self: *Module,
+ scope: *Scope,
+ decl_arena: *std.heap.ArenaAllocator,
+ typed_value: TypedValue,
+) !*Decl {
+ const name_index = self.getNextAnonNameIndex();
+ const scope_decl = scope.decl().?;
+ const name = try std.fmt.allocPrint(self.gpa, "{}__anon_{}", .{ scope_decl.name, name_index });
+ defer self.gpa.free(name);
+ const name_hash = scope.namespace().fullyQualifiedNameHash(name);
+ const src_hash: std.zig.SrcHash = undefined;
+ const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash);
+ const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
+
+ decl_arena_state.* = decl_arena.state;
+ new_decl.typed_value = .{
+ .most_recent = .{
+ .typed_value = typed_value,
+ .arena = decl_arena_state,
+ },
+ };
+ new_decl.analysis = .complete;
+ new_decl.generation = self.generation;
+
+ // TODO: This generates the Decl into the machine code file if it is of a type that is non-zero size.
+ // 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.comp.bin_file.allocateDeclIndexes(new_decl);
+ try self.comp.work_queue.writeItem(.{ .codegen_decl = new_decl });
+ }
+
+ return new_decl;
+}
+
+fn getNextAnonNameIndex(self: *Module) usize {
+ return @atomicRmw(usize, &self.next_anon_name_index, .Add, 1, .Monotonic);
+}
+
+pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl {
+ const namespace = scope.namespace();
+ const name_hash = namespace.fullyQualifiedNameHash(ident_name);
+ return self.decl_table.get(name_hash);
+}
+
+pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
+ const scope_decl = scope.decl().?;
+ try self.declareDeclDependency(scope_decl, decl);
+ self.ensureDeclAnalyzed(decl) catch |err| {
+ if (scope.cast(Scope.Block)) |block| {
+ if (block.func) |func| {
+ func.analysis = .dependency_failure;
+ } else {
+ block.decl.analysis = .dependency_failure;
+ }
+ } else {
+ scope_decl.analysis = .dependency_failure;
+ }
+ return err;
+ };
+
+ const decl_tv = try decl.typedValue();
+ if (decl_tv.val.tag() == .variable) {
+ return self.analyzeVarRef(scope, src, decl_tv);
+ }
+ const ty = try self.simplePtrType(scope, src, decl_tv.ty, false, .One);
+ const val_payload = try scope.arena().create(Value.Payload.DeclRef);
+ val_payload.* = .{ .decl = decl };
+
+ return self.constInst(scope, src, .{
+ .ty = ty,
+ .val = Value.initPayload(&val_payload.base),
+ });
+}
+
+fn analyzeVarRef(self: *Module, scope: *Scope, src: usize, tv: TypedValue) InnerError!*Inst {
+ const variable = tv.val.cast(Value.Payload.Variable).?.variable;
+
+ const ty = try self.simplePtrType(scope, src, tv.ty, variable.is_mutable, .One);
+ if (!variable.is_mutable and !variable.is_extern) {
+ const val_payload = try scope.arena().create(Value.Payload.RefVal);
+ val_payload.* = .{ .val = variable.init };
+ return self.constInst(scope, src, .{
+ .ty = ty,
+ .val = Value.initPayload(&val_payload.base),
+ });
+ }
+
+ const b = try self.requireRuntimeBlock(scope, src);
+ const inst = try b.arena.create(Inst.VarPtr);
+ inst.* = .{
+ .base = .{
+ .tag = .varptr,
+ .ty = ty,
+ .src = src,
+ },
+ .variable = variable,
+ };
+ try b.instructions.append(self.gpa, &inst.base);
+ return &inst.base;
+}
+
+pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst {
+ const elem_ty = switch (ptr.ty.zigTypeTag()) {
+ .Pointer => ptr.ty.elemType(),
+ else => return self.fail(scope, ptr_src, "expected pointer, found '{}'", .{ptr.ty}),
+ };
+ if (ptr.value()) |val| {
+ return self.constInst(scope, src, .{
+ .ty = elem_ty,
+ .val = try val.pointerDeref(scope.arena()),
+ });
+ }
+
+ const b = try self.requireRuntimeBlock(scope, src);
+ return self.addUnOp(b, src, elem_ty, .load, ptr);
+}
+
+pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst {
+ const decl = self.lookupDeclName(scope, decl_name) orelse
+ return self.fail(scope, src, "decl '{}' not found", .{decl_name});
+ return self.analyzeDeclRef(scope, src, decl);
+}
+
+pub fn wantSafety(self: *Module, scope: *Scope) bool {
+ // TODO take into account scope's safety overrides
+ return switch (self.optimizeMode()) {
+ .Debug => true,
+ .ReleaseSafe => true,
+ .ReleaseFast => false,
+ .ReleaseSmall => false,
+ };
+}
+
+pub fn analyzeIsNull(
+ self: *Module,
+ scope: *Scope,
+ src: usize,
+ operand: *Inst,
+ invert_logic: bool,
+) InnerError!*Inst {
+ if (operand.value()) |opt_val| {
+ const is_null = opt_val.isNull();
+ const bool_value = if (invert_logic) !is_null else is_null;
+ return self.constBool(scope, src, bool_value);
+ }
+ const b = try self.requireRuntimeBlock(scope, src);
+ const inst_tag: Inst.Tag = if (invert_logic) .isnonnull else .isnull;
+ return self.addUnOp(b, src, Type.initTag(.bool), inst_tag, operand);
+}
+
+pub fn analyzeIsErr(self: *Module, scope: *Scope, src: usize, operand: *Inst) InnerError!*Inst {
+ return self.fail(scope, src, "TODO implement analysis of iserr", .{});
+}
+
+pub fn analyzeSlice(self: *Module, scope: *Scope, src: usize, array_ptr: *Inst, start: *Inst, end_opt: ?*Inst, sentinel_opt: ?*Inst) InnerError!*Inst {
+ const ptr_child = switch (array_ptr.ty.zigTypeTag()) {
+ .Pointer => array_ptr.ty.elemType(),
+ else => return self.fail(scope, src, "expected pointer, found '{}'", .{array_ptr.ty}),
+ };
+
+ var array_type = ptr_child;
+ const elem_type = switch (ptr_child.zigTypeTag()) {
+ .Array => ptr_child.elemType(),
+ .Pointer => blk: {
+ if (ptr_child.isSinglePointer()) {
+ if (ptr_child.elemType().zigTypeTag() == .Array) {
+ array_type = ptr_child.elemType();
+ break :blk ptr_child.elemType().elemType();
+ }
+
+ return self.fail(scope, src, "slice of single-item pointer", .{});
+ }
+ break :blk ptr_child.elemType();
+ },
+ else => return self.fail(scope, src, "slice of non-array type '{}'", .{ptr_child}),
+ };
+
+ const slice_sentinel = if (sentinel_opt) |sentinel| blk: {
+ const casted = try self.coerce(scope, elem_type, sentinel);
+ break :blk try self.resolveConstValue(scope, casted);
+ } else null;
+
+ var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice;
+ var return_elem_type = elem_type;
+ if (end_opt) |end| {
+ if (end.value()) |end_val| {
+ if (start.value()) |start_val| {
+ const start_u64 = start_val.toUnsignedInt();
+ const end_u64 = end_val.toUnsignedInt();
+ if (start_u64 > end_u64) {
+ return self.fail(scope, src, "out of bounds slice", .{});
+ }
+
+ const len = end_u64 - start_u64;
+ const array_sentinel = if (array_type.zigTypeTag() == .Array and end_u64 == array_type.arrayLen())
+ array_type.sentinel()
+ else
+ slice_sentinel;
+ return_elem_type = try self.arrayType(scope, len, array_sentinel, elem_type);
+ return_ptr_size = .One;
+ }
+ }
+ }
+ const return_type = try self.ptrType(
+ scope,
+ src,
+ return_elem_type,
+ if (end_opt == null) slice_sentinel else null,
+ 0, // TODO alignment
+ 0,
+ 0,
+ !ptr_child.isConstPtr(),
+ ptr_child.isAllowzeroPtr(),
+ ptr_child.isVolatilePtr(),
+ return_ptr_size,
+ );
+
+ return self.fail(scope, src, "TODO implement analysis of slice", .{});
+}
+
+/// Asserts that lhs and rhs types are both numeric.
+pub fn cmpNumeric(
+ self: *Module,
+ scope: *Scope,
+ src: usize,
+ lhs: *Inst,
+ rhs: *Inst,
+ op: std.math.CompareOperator,
+) !*Inst {
+ assert(lhs.ty.isNumeric());
+ assert(rhs.ty.isNumeric());
+
+ const lhs_ty_tag = lhs.ty.zigTypeTag();
+ const rhs_ty_tag = rhs.ty.zigTypeTag();
+
+ if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) {
+ if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
+ return self.fail(scope, src, "vector length mismatch: {} and {}", .{
+ lhs.ty.arrayLen(),
+ rhs.ty.arrayLen(),
+ });
+ }
+ return self.fail(scope, src, "TODO implement support for vectors in cmpNumeric", .{});
+ } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) {
+ return self.fail(scope, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
+ lhs.ty,
+ rhs.ty,
+ });
+ }
+
+ if (lhs.value()) |lhs_val| {
+ if (rhs.value()) |rhs_val| {
+ return self.constBool(scope, src, Value.compare(lhs_val, op, rhs_val));
+ }
+ }
+
+ // TODO handle comparisons against lazy zero values
+ // Some values can be compared against zero without being runtime known or without forcing
+ // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to
+ // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout
+ // of this function if we don't need to.
+
+ // It must be a runtime comparison.
+ const b = try self.requireRuntimeBlock(scope, src);
+ // For floats, emit a float comparison instruction.
+ const lhs_is_float = switch (lhs_ty_tag) {
+ .Float, .ComptimeFloat => true,
+ else => false,
+ };
+ const rhs_is_float = switch (rhs_ty_tag) {
+ .Float, .ComptimeFloat => true,
+ else => false,
+ };
+ if (lhs_is_float and rhs_is_float) {
+ // Implicit cast the smaller one to the larger one.
+ const dest_type = x: {
+ if (lhs_ty_tag == .ComptimeFloat) {
+ break :x rhs.ty;
+ } else if (rhs_ty_tag == .ComptimeFloat) {
+ break :x lhs.ty;
+ }
+ if (lhs.ty.floatBits(self.getTarget()) >= rhs.ty.floatBits(self.getTarget())) {
+ break :x lhs.ty;
+ } else {
+ break :x rhs.ty;
+ }
+ };
+ const casted_lhs = try self.coerce(scope, dest_type, lhs);
+ const casted_rhs = try self.coerce(scope, dest_type, rhs);
+ return self.addBinOp(b, src, dest_type, Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
+ }
+ // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
+ // For mixed signed and unsigned integers, implicit cast both operands to a signed
+ // integer with + 1 bit.
+ // For mixed floats and integers, extract the integer part from the float, cast that to
+ // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
+ // add/subtract 1.
+ const lhs_is_signed = if (lhs.value()) |lhs_val|
+ lhs_val.compareWithZero(.lt)
+ else
+ (lhs.ty.isFloat() or lhs.ty.isSignedInt());
+ const rhs_is_signed = if (rhs.value()) |rhs_val|
+ rhs_val.compareWithZero(.lt)
+ else
+ (rhs.ty.isFloat() or rhs.ty.isSignedInt());
+ const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
+
+ var dest_float_type: ?Type = null;
+
+ var lhs_bits: usize = undefined;
+ if (lhs.value()) |lhs_val| {
+ if (lhs_val.isUndef())
+ return self.constUndef(scope, src, Type.initTag(.bool));
+ const is_unsigned = if (lhs_is_float) x: {
+ var bigint_space: Value.BigIntSpace = undefined;
+ var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(self.gpa);
+ defer bigint.deinit();
+ const zcmp = lhs_val.orderAgainstZero();
+ if (lhs_val.floatHasFraction()) {
+ switch (op) {
+ .eq => return self.constBool(scope, src, false),
+ .neq => return self.constBool(scope, src, true),
+ else => {},
+ }
+ if (zcmp == .lt) {
+ try bigint.addScalar(bigint.toConst(), -1);
+ } else {
+ try bigint.addScalar(bigint.toConst(), 1);
+ }
+ }
+ lhs_bits = bigint.toConst().bitCountTwosComp();
+ break :x (zcmp != .lt);
+ } else x: {
+ lhs_bits = lhs_val.intBitCountTwosComp();
+ break :x (lhs_val.orderAgainstZero() != .lt);
+ };
+ lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
+ } else if (lhs_is_float) {
+ dest_float_type = lhs.ty;
+ } else {
+ const int_info = lhs.ty.intInfo(self.getTarget());
+ lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
+ }
+
+ var rhs_bits: usize = undefined;
+ if (rhs.value()) |rhs_val| {
+ if (rhs_val.isUndef())
+ return self.constUndef(scope, src, Type.initTag(.bool));
+ const is_unsigned = if (rhs_is_float) x: {
+ var bigint_space: Value.BigIntSpace = undefined;
+ var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(self.gpa);
+ defer bigint.deinit();
+ const zcmp = rhs_val.orderAgainstZero();
+ if (rhs_val.floatHasFraction()) {
+ switch (op) {
+ .eq => return self.constBool(scope, src, false),
+ .neq => return self.constBool(scope, src, true),
+ else => {},
+ }
+ if (zcmp == .lt) {
+ try bigint.addScalar(bigint.toConst(), -1);
+ } else {
+ try bigint.addScalar(bigint.toConst(), 1);
+ }
+ }
+ rhs_bits = bigint.toConst().bitCountTwosComp();
+ break :x (zcmp != .lt);
+ } else x: {
+ rhs_bits = rhs_val.intBitCountTwosComp();
+ break :x (rhs_val.orderAgainstZero() != .lt);
+ };
+ rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
+ } else if (rhs_is_float) {
+ dest_float_type = rhs.ty;
+ } else {
+ const int_info = rhs.ty.intInfo(self.getTarget());
+ rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
+ }
+
+ const dest_type = if (dest_float_type) |ft| ft else blk: {
+ const max_bits = std.math.max(lhs_bits, rhs_bits);
+ const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) {
+ error.Overflow => return self.fail(scope, src, "{} exceeds maximum integer bit count", .{max_bits}),
+ };
+ break :blk try self.makeIntType(scope, dest_int_is_signed, casted_bits);
+ };
+ const casted_lhs = try self.coerce(scope, dest_type, lhs);
+ const casted_rhs = try self.coerce(scope, dest_type, rhs);
+
+ return self.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
+}
+
+fn wrapOptional(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
+ if (inst.value()) |val| {
+ return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
+ }
+
+ const b = try self.requireRuntimeBlock(scope, inst.src);
+ return self.addUnOp(b, inst.src, dest_type, .wrap_optional, inst);
+}
+
+fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
+ if (signed) {
+ const int_payload = try scope.arena().create(Type.Payload.IntSigned);
+ int_payload.* = .{ .bits = bits };
+ return Type.initPayload(&int_payload.base);
+ } else {
+ const int_payload = try scope.arena().create(Type.Payload.IntUnsigned);
+ int_payload.* = .{ .bits = bits };
+ return Type.initPayload(&int_payload.base);
+ }
+}
+
+pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {
+ if (instructions.len == 0)
+ return Type.initTag(.noreturn);
+
+ if (instructions.len == 1)
+ return instructions[0].ty;
+
+ var prev_inst = instructions[0];
+ for (instructions[1..]) |next_inst| {
+ if (next_inst.ty.eql(prev_inst.ty))
+ continue;
+ if (next_inst.ty.zigTypeTag() == .NoReturn)
+ continue;
+ if (prev_inst.ty.zigTypeTag() == .NoReturn) {
+ prev_inst = next_inst;
+ continue;
+ }
+ if (next_inst.ty.zigTypeTag() == .Undefined)
+ continue;
+ if (prev_inst.ty.zigTypeTag() == .Undefined) {
+ prev_inst = next_inst;
+ continue;
+ }
+ if (prev_inst.ty.isInt() and
+ next_inst.ty.isInt() and
+ prev_inst.ty.isSignedInt() == next_inst.ty.isSignedInt())
+ {
+ 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.getTarget()) < next_inst.ty.floatBits(self.getTarget())) {
+ prev_inst = next_inst;
+ }
+ continue;
+ }
+
+ // TODO error notes pointing out each type
+ return self.fail(scope, next_inst.src, "incompatible types: '{}' and '{}'", .{ prev_inst.ty, next_inst.ty });
+ }
+
+ return prev_inst.ty;
+}
+
+pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
+ // If the types are the same, we can return the operand.
+ if (dest_type.eql(inst.ty))
+ return inst;
+
+ const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
+ if (in_memory_result == .ok) {
+ return self.bitcast(scope, dest_type, inst);
+ }
+
+ // undefined to anything
+ if (inst.value()) |val| {
+ if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) {
+ return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
+ }
+ }
+ assert(inst.ty.zigTypeTag() != .Undefined);
+
+ // null to ?T
+ if (dest_type.zigTypeTag() == .Optional and inst.ty.zigTypeTag() == .Null) {
+ return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });
+ }
+
+ // T to ?T
+ if (dest_type.zigTypeTag() == .Optional) {
+ var buf: Type.Payload.PointerSimple = undefined;
+ const child_type = dest_type.optionalChild(&buf);
+ if (child_type.eql(inst.ty)) {
+ return self.wrapOptional(scope, dest_type, inst);
+ } else if (try self.coerceNum(scope, child_type, inst)) |some| {
+ return self.wrapOptional(scope, dest_type, some);
+ }
+ }
+
+ // *[N]T to []T
+ if (inst.ty.isSinglePointer() and dest_type.isSlice() and
+ (!inst.ty.isConstPtr() or dest_type.isConstPtr()))
+ {
+ const array_type = inst.ty.elemType();
+ const dst_elem_type = dest_type.elemType();
+ if (array_type.zigTypeTag() == .Array and
+ coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok)
+ {
+ return self.coerceArrayPtrToSlice(scope, dest_type, inst);
+ }
+ }
+
+ // comptime known number to other number
+ if (try self.coerceNum(scope, dest_type, inst)) |some|
+ return some;
+
+ // integer widening
+ if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) {
+ assert(inst.value() == null); // handled above
+
+ 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
+ (src_info.signed and !dst_info.signed and dst_info.bits > src_info.bits))
+ {
+ const b = try self.requireRuntimeBlock(scope, inst.src);
+ return self.addUnOp(b, inst.src, dest_type, .intcast, inst);
+ }
+ }
+
+ // float widening
+ if (inst.ty.zigTypeTag() == .Float and dest_type.zigTypeTag() == .Float) {
+ assert(inst.value() == null); // handled above
+
+ 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);
+ }
+ }
+
+ return self.fail(scope, inst.src, "expected {}, found {}", .{ dest_type, inst.ty });
+}
+
+pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !?*Inst {
+ const val = inst.value() orelse return null;
+ const src_zig_tag = inst.ty.zigTypeTag();
+ const dst_zig_tag = dest_type.zigTypeTag();
+
+ if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {
+ if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
+ if (val.floatHasFraction()) {
+ return self.fail(scope, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty });
+ }
+ 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.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.getTarget()) catch |err| switch (err) {
+ error.Overflow => return self.fail(
+ scope,
+ inst.src,
+ "cast of value {} to type '{}' loses information",
+ .{ val, dest_type },
+ ),
+ error.OutOfMemory => return error.OutOfMemory,
+ };
+ return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = res });
+ } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
+ return self.fail(scope, inst.src, "TODO int to float", .{});
+ }
+ }
+ return null;
+}
+
+pub fn storePtr(self: *Module, scope: *Scope, src: usize, ptr: *Inst, uncasted_value: *Inst) !*Inst {
+ if (ptr.ty.isConstPtr())
+ return self.fail(scope, src, "cannot assign to constant", .{});
+
+ const elem_ty = ptr.ty.elemType();
+ const value = try self.coerce(scope, elem_ty, uncasted_value);
+ if (elem_ty.onePossibleValue() != null)
+ return self.constVoid(scope, src);
+
+ // TODO handle comptime pointer writes
+ // TODO handle if the element type requires comptime
+
+ const b = try self.requireRuntimeBlock(scope, src);
+ return self.addBinOp(b, src, Type.initTag(.void), .store, ptr, value);
+}
+
+pub fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
+ if (inst.value()) |val| {
+ // Keep the comptime Value representation; take the new type.
+ return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
+ }
+ // TODO validate the type size and other compile errors
+ const b = try self.requireRuntimeBlock(scope, inst.src);
+ return self.addUnOp(b, inst.src, dest_type, .bitcast, inst);
+}
+
+fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
+ if (inst.value()) |val| {
+ // The comptime Value representation is compatible with both types.
+ return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
+ }
+ return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
+}
+
+pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError {
+ @setCold(true);
+ const err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args);
+ return self.failWithOwnedErrorMsg(scope, src, err_msg);
+}
+
+pub fn failTok(
+ self: *Module,
+ scope: *Scope,
+ token_index: ast.TokenIndex,
+ comptime format: []const u8,
+ args: anytype,
+) InnerError {
+ @setCold(true);
+ const src = scope.tree().token_locs[token_index].start;
+ return self.fail(scope, src, format, args);
+}
+
+pub fn failNode(
+ self: *Module,
+ scope: *Scope,
+ ast_node: *ast.Node,
+ comptime format: []const u8,
+ args: anytype,
+) InnerError {
+ @setCold(true);
+ const src = scope.tree().token_locs[ast_node.firstToken()].start;
+ return self.fail(scope, src, format, args);
+}
+
+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);
+ try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
+ }
+ switch (scope.tag) {
+ .decl => {
+ const decl = scope.cast(Scope.DeclAnalysis).?.decl;
+ decl.analysis = .sema_failure;
+ decl.generation = self.generation;
+ self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg);
+ },
+ .block => {
+ const block = scope.cast(Scope.Block).?;
+ if (block.func) |func| {
+ func.analysis = .sema_failure;
+ } else {
+ block.decl.analysis = .sema_failure;
+ block.decl.generation = self.generation;
+ }
+ self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg);
+ },
+ .gen_zir => {
+ const gen_zir = scope.cast(Scope.GenZIR).?;
+ gen_zir.decl.analysis = .sema_failure;
+ gen_zir.decl.generation = self.generation;
+ self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
+ },
+ .local_val => {
+ const gen_zir = scope.cast(Scope.LocalVal).?.gen_zir;
+ gen_zir.decl.analysis = .sema_failure;
+ gen_zir.decl.generation = self.generation;
+ self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
+ },
+ .local_ptr => {
+ const gen_zir = scope.cast(Scope.LocalPtr).?.gen_zir;
+ gen_zir.decl.analysis = .sema_failure;
+ gen_zir.decl.generation = self.generation;
+ self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
+ },
+ .zir_module => {
+ const zir_module = scope.cast(Scope.ZIRModule).?;
+ zir_module.status = .loaded_sema_failure;
+ self.failed_files.putAssumeCapacityNoClobber(scope, err_msg);
+ },
+ .file => unreachable,
+ .container => unreachable,
+ }
+ return error.AnalysisFail;
+}
+
+const InMemoryCoercionResult = enum {
+ ok,
+ no_match,
+};
+
+fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult {
+ if (dest_type.eql(src_type))
+ return .ok;
+
+ // TODO: implement more of this function
+
+ return .no_match;
+}
+
+fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool {
+ return @bitCast(u128, a) == @bitCast(u128, b);
+}
+
+pub fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
+ // TODO is this a performance issue? maybe we should try the operation without
+ // resorting to BigInt first.
+ var lhs_space: Value.BigIntSpace = undefined;
+ var rhs_space: Value.BigIntSpace = undefined;
+ const lhs_bigint = lhs.toBigInt(&lhs_space);
+ const rhs_bigint = rhs.toBigInt(&rhs_space);
+ const limbs = try allocator.alloc(
+ std.math.big.Limb,
+ std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
+ );
+ var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
+ result_bigint.add(lhs_bigint, rhs_bigint);
+ const result_limbs = result_bigint.limbs[0..result_bigint.len];
+
+ const val_payload = if (result_bigint.positive) blk: {
+ const val_payload = try allocator.create(Value.Payload.IntBigPositive);
+ val_payload.* = .{ .limbs = result_limbs };
+ break :blk &val_payload.base;
+ } else blk: {
+ const val_payload = try allocator.create(Value.Payload.IntBigNegative);
+ val_payload.* = .{ .limbs = result_limbs };
+ break :blk &val_payload.base;
+ };
+
+ return Value.initPayload(val_payload);
+}
+
+pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
+ // TODO is this a performance issue? maybe we should try the operation without
+ // resorting to BigInt first.
+ var lhs_space: Value.BigIntSpace = undefined;
+ var rhs_space: Value.BigIntSpace = undefined;
+ const lhs_bigint = lhs.toBigInt(&lhs_space);
+ const rhs_bigint = rhs.toBigInt(&rhs_space);
+ const limbs = try allocator.alloc(
+ std.math.big.Limb,
+ std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
+ );
+ var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
+ result_bigint.sub(lhs_bigint, rhs_bigint);
+ const result_limbs = result_bigint.limbs[0..result_bigint.len];
+
+ const val_payload = if (result_bigint.positive) blk: {
+ const val_payload = try allocator.create(Value.Payload.IntBigPositive);
+ val_payload.* = .{ .limbs = result_limbs };
+ break :blk &val_payload.base;
+ } else blk: {
+ const val_payload = try allocator.create(Value.Payload.IntBigNegative);
+ val_payload.* = .{ .limbs = result_limbs };
+ break :blk &val_payload.base;
+ };
+
+ return Value.initPayload(val_payload);
+}
+
+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.getTarget()),
+ };
+
+ const allocator = scope.arena();
+ const val_payload = switch (bit_count) {
+ 16 => {
+ return self.fail(scope, src, "TODO Implement addition for soft floats", .{});
+ },
+ 32 => blk: {
+ const lhs_val = lhs.toFloat(f32);
+ const rhs_val = rhs.toFloat(f32);
+ const val_payload = try allocator.create(Value.Payload.Float_32);
+ val_payload.* = .{ .val = lhs_val + rhs_val };
+ break :blk &val_payload.base;
+ },
+ 64 => blk: {
+ const lhs_val = lhs.toFloat(f64);
+ const rhs_val = rhs.toFloat(f64);
+ const val_payload = try allocator.create(Value.Payload.Float_64);
+ val_payload.* = .{ .val = lhs_val + rhs_val };
+ break :blk &val_payload.base;
+ },
+ 128 => {
+ return self.fail(scope, src, "TODO Implement addition for big floats", .{});
+ },
+ else => unreachable,
+ };
+
+ return Value.initPayload(val_payload);
+}
+
+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.getTarget()),
+ };
+
+ const allocator = scope.arena();
+ const val_payload = switch (bit_count) {
+ 16 => {
+ return self.fail(scope, src, "TODO Implement substraction for soft floats", .{});
+ },
+ 32 => blk: {
+ const lhs_val = lhs.toFloat(f32);
+ const rhs_val = rhs.toFloat(f32);
+ const val_payload = try allocator.create(Value.Payload.Float_32);
+ val_payload.* = .{ .val = lhs_val - rhs_val };
+ break :blk &val_payload.base;
+ },
+ 64 => blk: {
+ const lhs_val = lhs.toFloat(f64);
+ const rhs_val = rhs.toFloat(f64);
+ const val_payload = try allocator.create(Value.Payload.Float_64);
+ val_payload.* = .{ .val = lhs_val - rhs_val };
+ break :blk &val_payload.base;
+ },
+ 128 => {
+ return self.fail(scope, src, "TODO Implement substraction for big floats", .{});
+ },
+ else => unreachable,
+ };
+
+ return Value.initPayload(val_payload);
+}
+
+pub fn simplePtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type, mutable: bool, size: std.builtin.TypeInfo.Pointer.Size) Allocator.Error!Type {
+ if (!mutable and size == .Slice and elem_ty.eql(Type.initTag(.u8))) {
+ return Type.initTag(.const_slice_u8);
+ }
+ // TODO stage1 type inference bug
+ const T = Type.Tag;
+
+ const type_payload = try scope.arena().create(Type.Payload.PointerSimple);
+ type_payload.* = .{
+ .base = .{
+ .tag = switch (size) {
+ .One => if (mutable) T.single_mut_pointer else T.single_const_pointer,
+ .Many => if (mutable) T.many_mut_pointer else T.many_const_pointer,
+ .C => if (mutable) T.c_mut_pointer else T.c_const_pointer,
+ .Slice => if (mutable) T.mut_slice else T.const_slice,
+ },
+ },
+ .pointee_type = elem_ty,
+ };
+ return Type.initPayload(&type_payload.base);
+}
+
+pub fn ptrType(
+ self: *Module,
+ scope: *Scope,
+ src: usize,
+ elem_ty: Type,
+ sentinel: ?Value,
+ @"align": u32,
+ bit_offset: u16,
+ host_size: u16,
+ mutable: bool,
+ @"allowzero": bool,
+ @"volatile": bool,
+ size: std.builtin.TypeInfo.Pointer.Size,
+) Allocator.Error!Type {
+ assert(host_size == 0 or bit_offset < host_size * 8);
+
+ // TODO check if type can be represented by simplePtrType
+ const type_payload = try scope.arena().create(Type.Payload.Pointer);
+ type_payload.* = .{
+ .pointee_type = elem_ty,
+ .sentinel = sentinel,
+ .@"align" = @"align",
+ .bit_offset = bit_offset,
+ .host_size = host_size,
+ .@"allowzero" = @"allowzero",
+ .mutable = mutable,
+ .@"volatile" = @"volatile",
+ .size = size,
+ };
+ return Type.initPayload(&type_payload.base);
+}
+
+pub fn optionalType(self: *Module, scope: *Scope, child_type: Type) Allocator.Error!Type {
+ return Type.initPayload(switch (child_type.tag()) {
+ .single_const_pointer => blk: {
+ const payload = try scope.arena().create(Type.Payload.PointerSimple);
+ payload.* = .{
+ .base = .{ .tag = .optional_single_const_pointer },
+ .pointee_type = child_type.elemType(),
+ };
+ break :blk &payload.base;
+ },
+ .single_mut_pointer => blk: {
+ const payload = try scope.arena().create(Type.Payload.PointerSimple);
+ payload.* = .{
+ .base = .{ .tag = .optional_single_mut_pointer },
+ .pointee_type = child_type.elemType(),
+ };
+ break :blk &payload.base;
+ },
+ else => blk: {
+ const payload = try scope.arena().create(Type.Payload.Optional);
+ payload.* = .{
+ .child_type = child_type,
+ };
+ break :blk &payload.base;
+ },
+ });
+}
+
+pub fn arrayType(self: *Module, scope: *Scope, len: u64, sentinel: ?Value, elem_type: Type) Allocator.Error!Type {
+ if (elem_type.eql(Type.initTag(.u8))) {
+ if (sentinel) |some| {
+ if (some.eql(Value.initTag(.zero))) {
+ const payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0);
+ payload.* = .{
+ .len = len,
+ };
+ return Type.initPayload(&payload.base);
+ }
+ } else {
+ const payload = try scope.arena().create(Type.Payload.Array_u8);
+ payload.* = .{
+ .len = len,
+ };
+ return Type.initPayload(&payload.base);
+ }
+ }
+
+ if (sentinel) |some| {
+ const payload = try scope.arena().create(Type.Payload.ArraySentinel);
+ payload.* = .{
+ .len = len,
+ .sentinel = some,
+ .elem_type = elem_type,
+ };
+ return Type.initPayload(&payload.base);
+ }
+
+ const payload = try scope.arena().create(Type.Payload.Array);
+ payload.* = .{
+ .len = len,
+ .elem_type = elem_type,
+ };
+ return Type.initPayload(&payload.base);
+}
+
+pub fn errorUnionType(self: *Module, scope: *Scope, error_set: Type, payload: Type) Allocator.Error!Type {
+ assert(error_set.zigTypeTag() == .ErrorSet);
+ if (error_set.eql(Type.initTag(.anyerror)) and payload.eql(Type.initTag(.void))) {
+ return Type.initTag(.anyerror_void_error_union);
+ }
+
+ const result = try scope.arena().create(Type.Payload.ErrorUnion);
+ result.* = .{
+ .error_set = error_set,
+ .payload = payload,
+ };
+ return Type.initPayload(&result.base);
+}
+
+pub fn anyframeType(self: *Module, scope: *Scope, return_type: Type) Allocator.Error!Type {
+ const result = try scope.arena().create(Type.Payload.AnyFrame);
+ result.* = .{
+ .return_type = return_type,
+ };
+ return Type.initPayload(&result.base);
+}
+
+pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
+ const zir_module = scope.namespace();
+ const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source");
+ const loc = std.zig.findLineColumn(source, inst.src);
+ if (inst.tag == .constant) {
+ std.debug.print("constant ty={} val={} src={}:{}:{}\n", .{
+ inst.ty,
+ inst.castTag(.constant).?.val,
+ zir_module.subFilePath(),
+ loc.line + 1,
+ loc.column + 1,
+ });
+ } else if (inst.deaths == 0) {
+ std.debug.print("{} ty={} src={}:{}:{}\n", .{
+ @tagName(inst.tag),
+ inst.ty,
+ zir_module.subFilePath(),
+ loc.line + 1,
+ loc.column + 1,
+ });
+ } else {
+ std.debug.print("{} ty={} deaths={b} src={}:{}:{}\n", .{
+ @tagName(inst.tag),
+ inst.ty,
+ inst.deaths,
+ zir_module.subFilePath(),
+ loc.line + 1,
+ loc.column + 1,
+ });
+ }
+}
+
+pub const PanicId = enum {
+ unreach,
+ unwrap_null,
+};
+
+pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void {
+ const block_inst = try parent_block.arena.create(Inst.Block);
+ block_inst.* = .{
+ .base = .{
+ .tag = Inst.Block.base_tag,
+ .ty = Type.initTag(.void),
+ .src = ok.src,
+ },
+ .body = .{
+ .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the condbr.
+ },
+ };
+
+ const ok_body: ir.Body = .{
+ .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the brvoid.
+ };
+ const brvoid = try parent_block.arena.create(Inst.BrVoid);
+ brvoid.* = .{
+ .base = .{
+ .tag = .brvoid,
+ .ty = Type.initTag(.noreturn),
+ .src = ok.src,
+ },
+ .block = block_inst,
+ };
+ ok_body.instructions[0] = &brvoid.base;
+
+ var fail_block: Scope.Block = .{
+ .parent = parent_block,
+ .func = parent_block.func,
+ .decl = parent_block.decl,
+ .instructions = .{},
+ .arena = parent_block.arena,
+ .is_comptime = parent_block.is_comptime,
+ };
+ defer fail_block.instructions.deinit(mod.gpa);
+
+ _ = try mod.safetyPanic(&fail_block, ok.src, panic_id);
+
+ const fail_body: ir.Body = .{ .instructions = try parent_block.arena.dupe(*Inst, fail_block.instructions.items) };
+
+ const condbr = try parent_block.arena.create(Inst.CondBr);
+ condbr.* = .{
+ .base = .{
+ .tag = .condbr,
+ .ty = Type.initTag(.noreturn),
+ .src = ok.src,
+ },
+ .condition = ok,
+ .then_body = ok_body,
+ .else_body = fail_body,
+ };
+ block_inst.body.instructions[0] = &condbr.base;
+
+ try parent_block.instructions.append(mod.gpa, &block_inst.base);
+}
+
+pub fn safetyPanic(mod: *Module, block: *Scope.Block, src: usize, panic_id: PanicId) !*Inst {
+ // TODO Once we have a panic function to call, call it here instead of breakpoint.
+ _ = 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 0000000000000000000000000000000000000000..8a0e89f88358f755f114683c83acb59ecd248994
--- /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/TypedValue.zig b/src/TypedValue.zig
new file mode 100644
index 0000000000000000000000000000000000000000..48b2c04970d15593a420f73d40967f003cbec9d9
--- /dev/null
+++ b/src/TypedValue.zig
@@ -0,0 +1,31 @@
+const std = @import("std");
+const Type = @import("type.zig").Type;
+const Value = @import("value.zig").Value;
+const Allocator = std.mem.Allocator;
+const TypedValue = @This();
+
+ty: Type,
+val: Value,
+
+/// Memory management for TypedValue. The main purpose of this type
+/// is to be small and have a deinit() function to free associated resources.
+pub const Managed = struct {
+ /// If the tag value is less than Tag.no_payload_count, then no pointer
+ /// dereference is needed.
+ typed_value: TypedValue,
+ /// If this is `null` then there is no memory management needed.
+ arena: ?*std.heap.ArenaAllocator.State = null,
+
+ pub fn deinit(self: *Managed, allocator: *Allocator) void {
+ if (self.arena) |a| a.promote(allocator).deinit();
+ self.* = undefined;
+ }
+};
+
+/// Assumes arena allocation. Does a recursive copy.
+pub fn copy(self: TypedValue, allocator: *Allocator) error{OutOfMemory}!TypedValue {
+ return TypedValue{
+ .ty = try self.ty.copy(allocator),
+ .val = try self.val.copy(allocator),
+ };
+}
diff --git a/src/all_types.hpp b/src/all_types.hpp
deleted file mode 100644
index 1fa04f2b79fd1a7477bac41ea2df4e3d5f5f29b9..0000000000000000000000000000000000000000
--- a/src/all_types.hpp
+++ /dev/null
@@ -1,4772 +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_ALL_TYPES_HPP
-#define ZIG_ALL_TYPES_HPP
-
-#include "list.hpp"
-#include "buffer.hpp"
-#include "cache_hash.hpp"
-#include "zig_llvm.h"
-#include "hash_map.hpp"
-#include "errmsg.hpp"
-#include "bigint.hpp"
-#include "bigfloat.hpp"
-#include "target.hpp"
-#include "tokenizer.hpp"
-
-#ifndef NDEBUG
-#define DBG_MACRO_NO_WARNING
-#include
-#endif
-
-struct AstNode;
-struct ZigFn;
-struct Scope;
-struct ScopeBlock;
-struct ScopeFnDef;
-struct ScopeExpr;
-struct ZigType;
-struct ZigVar;
-struct ErrorTableEntry;
-struct BuiltinFnEntry;
-struct TypeStructField;
-struct CodeGen;
-struct ZigValue;
-struct IrInst;
-struct IrInstSrc;
-struct IrInstGen;
-struct IrInstGenCast;
-struct IrInstGenAlloca;
-struct IrInstGenCall;
-struct IrInstGenAwait;
-struct IrBasicBlockSrc;
-struct IrBasicBlockGen;
-struct ScopeDecls;
-struct ZigWindowsSDK;
-struct Tld;
-struct TldExport;
-struct IrAnalyze;
-struct ResultLoc;
-struct ResultLocPeer;
-struct ResultLocPeerParent;
-struct ResultLocBitCast;
-struct ResultLocCast;
-struct ResultLocReturn;
-struct IrExecutableGen;
-
-enum FileExt {
- FileExtUnknown,
- FileExtAsm,
- FileExtC,
- FileExtCpp,
- FileExtHeader,
- FileExtLLVMIr,
- FileExtLLVMBitCode,
-};
-
-enum PtrLen {
- PtrLenUnknown,
- PtrLenSingle,
- PtrLenC,
-};
-
-enum CallingConvention {
- CallingConventionUnspecified,
- CallingConventionC,
- CallingConventionCold,
- CallingConventionNaked,
- CallingConventionAsync,
- CallingConventionInterrupt,
- CallingConventionSignal,
- CallingConventionStdcall,
- CallingConventionFastcall,
- CallingConventionVectorcall,
- CallingConventionThiscall,
- CallingConventionAPCS,
- CallingConventionAAPCS,
- CallingConventionAAPCSVFP,
-};
-
-// This one corresponds to the builtin.zig enum.
-enum BuiltinPtrSize {
- BuiltinPtrSizeOne,
- BuiltinPtrSizeMany,
- BuiltinPtrSizeSlice,
- BuiltinPtrSizeC,
-};
-
-enum UndefAllowed {
- UndefOk,
- UndefBad,
- LazyOkNoUndef,
- LazyOk,
-};
-
-enum X64CABIClass {
- X64CABIClass_Unknown,
- X64CABIClass_MEMORY,
- X64CABIClass_MEMORY_nobyval,
- X64CABIClass_INTEGER,
- X64CABIClass_SSE,
-};
-
-struct IrExecutableSrc {
- ZigList basic_block_list;
- Buf *name;
- ZigFn *name_fn;
- size_t mem_slot_count;
- size_t next_debug_id;
- size_t *backward_branch_count;
- size_t *backward_branch_quota;
- ZigFn *fn_entry;
- Buf *c_import_buf;
- AstNode *source_node;
- IrExecutableGen *parent_exec;
- IrAnalyze *analysis;
- Scope *begin_scope;
- ErrorMsg *first_err_trace_msg;
- ZigList tld_list;
-
- bool is_inline;
- bool is_generic_instantiation;
- bool need_err_code_spill;
-
- // This is a function for use in the debugger to print
- // the source location.
- void src();
-};
-
-struct IrExecutableGen {
- ZigList basic_block_list;
- Buf *name;
- ZigFn *name_fn;
- size_t mem_slot_count;
- size_t next_debug_id;
- size_t *backward_branch_count;
- size_t *backward_branch_quota;
- ZigFn *fn_entry;
- Buf *c_import_buf;
- AstNode *source_node;
- IrExecutableGen *parent_exec;
- IrExecutableSrc *source_exec;
- Scope *begin_scope;
- ErrorMsg *first_err_trace_msg;
- ZigList tld_list;
-
- bool is_inline;
- bool is_generic_instantiation;
- bool need_err_code_spill;
-
- // This is a function for use in the debugger to print
- // the source location.
- void src();
-};
-
-enum OutType {
- OutTypeUnknown,
- OutTypeExe,
- OutTypeLib,
- OutTypeObj,
-};
-
-enum ConstParentId {
- ConstParentIdNone,
- ConstParentIdStruct,
- ConstParentIdErrUnionCode,
- ConstParentIdErrUnionPayload,
- ConstParentIdOptionalPayload,
- ConstParentIdArray,
- ConstParentIdUnion,
- ConstParentIdScalar,
-};
-
-struct ConstParent {
- ConstParentId id;
-
- union {
- struct {
- ZigValue *array_val;
- size_t elem_index;
- } p_array;
- struct {
- ZigValue *struct_val;
- size_t field_index;
- } p_struct;
- struct {
- ZigValue *err_union_val;
- } p_err_union_code;
- struct {
- ZigValue *err_union_val;
- } p_err_union_payload;
- struct {
- ZigValue *optional_val;
- } p_optional_payload;
- struct {
- ZigValue *union_val;
- } p_union;
- struct {
- ZigValue *scalar_val;
- } p_scalar;
- } data;
-};
-
-struct ConstStructValue {
- ZigValue **fields;
-};
-
-struct ConstUnionValue {
- BigInt tag;
- ZigValue *payload;
-};
-
-enum ConstArraySpecial {
- ConstArraySpecialNone,
- ConstArraySpecialUndef,
- ConstArraySpecialBuf,
-};
-
-struct ConstArrayValue {
- ConstArraySpecial special;
- union {
- struct {
- ZigValue *elements;
- } s_none;
- Buf *s_buf;
- } data;
-};
-
-enum ConstPtrSpecial {
- // Enforce explicitly setting this ID by making the zero value invalid.
- ConstPtrSpecialInvalid,
- // The pointer is a reference to a single object.
- ConstPtrSpecialRef,
- // The pointer points to an element in an underlying array.
- // Not to be confused with ConstPtrSpecialSubArray.
- ConstPtrSpecialBaseArray,
- // The pointer points to a field in an underlying struct.
- ConstPtrSpecialBaseStruct,
- // The pointer points to the error set field of an error union
- ConstPtrSpecialBaseErrorUnionCode,
- // The pointer points to the payload field of an error union
- ConstPtrSpecialBaseErrorUnionPayload,
- // The pointer points to the payload field of an optional
- ConstPtrSpecialBaseOptionalPayload,
- // This means that we did a compile-time pointer reinterpret and we cannot
- // understand the value of pointee at compile time. However, we will still
- // emit a binary with a compile time known address.
- // In this case index is the numeric address value.
- ConstPtrSpecialHardCodedAddr,
- // This means that the pointer represents memory of assigning to _.
- // That is, storing discards the data, and loading is invalid.
- ConstPtrSpecialDiscard,
- // This is actually a function.
- ConstPtrSpecialFunction,
- // This means the pointer is null. This is only allowed when the type is ?*T.
- // We use this instead of ConstPtrSpecialHardCodedAddr because often we check
- // for that value to avoid doing comptime work.
- // We need the data layout for ConstCastOnly == true
- // types to be the same, so all optionals of pointer types use x_ptr
- // instead of x_optional.
- ConstPtrSpecialNull,
- // The pointer points to a sub-array (not an individual element).
- // Not to be confused with ConstPtrSpecialBaseArray. However, it uses the same
- // union payload struct (base_array).
- ConstPtrSpecialSubArray,
-};
-
-enum ConstPtrMut {
- // The pointer points to memory that is known at compile time and immutable.
- ConstPtrMutComptimeConst,
- // This means that the pointer points to memory used by a comptime variable,
- // so attempting to write a non-compile-time known value is an error
- // But the underlying value is allowed to change at compile time.
- ConstPtrMutComptimeVar,
- // The pointer points to memory that is known only at runtime.
- // For example it may point to the initializer value of a variable.
- ConstPtrMutRuntimeVar,
- // The pointer points to memory for which it must be inferred whether the
- // value is comptime known or not.
- ConstPtrMutInfer,
-};
-
-struct ConstPtrValue {
- ConstPtrSpecial special;
- ConstPtrMut mut;
-
- union {
- struct {
- ZigValue *pointee;
- } ref;
- struct {
- ZigValue *array_val;
- size_t elem_index;
- } base_array;
- struct {
- ZigValue *struct_val;
- size_t field_index;
- } base_struct;
- struct {
- ZigValue *err_union_val;
- } base_err_union_code;
- struct {
- ZigValue *err_union_val;
- } base_err_union_payload;
- struct {
- ZigValue *optional_val;
- } base_optional_payload;
- struct {
- uint64_t addr;
- } hard_coded_addr;
- struct {
- ZigFn *fn_entry;
- } fn;
- } data;
-};
-
-struct ConstErrValue {
- ZigValue *error_set;
- ZigValue *payload;
-};
-
-struct ConstBoundFnValue {
- ZigFn *fn;
- IrInstGen *first_arg;
- IrInst *first_arg_src;
-};
-
-struct ConstArgTuple {
- size_t start_index;
- size_t end_index;
-};
-
-enum ConstValSpecial {
- ConstValSpecialRuntime,
- ConstValSpecialStatic,
- ConstValSpecialUndef,
- ConstValSpecialLazy,
-};
-
-enum RuntimeHintErrorUnion {
- RuntimeHintErrorUnionUnknown,
- RuntimeHintErrorUnionError,
- RuntimeHintErrorUnionNonError,
-};
-
-enum RuntimeHintOptional {
- RuntimeHintOptionalUnknown,
- RuntimeHintOptionalNull, // TODO is this value even possible? if this is the case it might mean the const value is compile time known.
- RuntimeHintOptionalNonNull,
-};
-
-enum RuntimeHintPtr {
- RuntimeHintPtrUnknown,
- RuntimeHintPtrStack,
- RuntimeHintPtrNonStack,
-};
-
-enum RuntimeHintSliceId {
- RuntimeHintSliceIdUnknown,
- RuntimeHintSliceIdLen,
-};
-
-struct RuntimeHintSlice {
- enum RuntimeHintSliceId id;
- uint64_t len;
-};
-
-enum LazyValueId {
- LazyValueIdInvalid,
- LazyValueIdAlignOf,
- LazyValueIdSizeOf,
- LazyValueIdPtrType,
- LazyValueIdOptType,
- LazyValueIdSliceType,
- LazyValueIdFnType,
- LazyValueIdErrUnionType,
- LazyValueIdArrayType,
- LazyValueIdTypeInfoDecls,
-};
-
-struct LazyValue {
- LazyValueId id;
-};
-
-struct LazyValueTypeInfoDecls {
- LazyValue base;
-
- IrAnalyze *ira;
-
- ScopeDecls *decls_scope;
- IrInst *source_instr;
-};
-
-struct LazyValueAlignOf {
- LazyValue base;
-
- IrAnalyze *ira;
- IrInstGen *target_type;
-};
-
-struct LazyValueSizeOf {
- LazyValue base;
-
- IrAnalyze *ira;
- IrInstGen *target_type;
-
- bool bit_size;
-};
-
-struct LazyValueSliceType {
- LazyValue base;
-
- IrAnalyze *ira;
- IrInstGen *sentinel; // can be null
- IrInstGen *elem_type;
- IrInstGen *align_inst; // can be null
-
- bool is_const;
- bool is_volatile;
- bool is_allowzero;
-};
-
-struct LazyValueArrayType {
- LazyValue base;
-
- IrAnalyze *ira;
- IrInstGen *sentinel; // can be null
- IrInstGen *elem_type;
- uint64_t length;
-};
-
-struct LazyValuePtrType {
- LazyValue base;
-
- IrAnalyze *ira;
- IrInstGen *sentinel; // can be null
- IrInstGen *elem_type;
- IrInstGen *align_inst; // can be null
-
- PtrLen ptr_len;
- uint32_t bit_offset_in_host;
-
- uint32_t host_int_bytes;
- bool is_const;
- bool is_volatile;
- bool is_allowzero;
-};
-
-struct LazyValueOptType {
- LazyValue base;
-
- IrAnalyze *ira;
- IrInstGen *payload_type;
-};
-
-struct LazyValueFnType {
- LazyValue base;
-
- IrAnalyze *ira;
- AstNode *proto_node;
- IrInstGen **param_types;
- IrInstGen *align_inst; // can be null
- IrInstGen *return_type;
-
- CallingConvention cc;
- bool is_generic;
-};
-
-struct LazyValueErrUnionType {
- LazyValue base;
-
- IrAnalyze *ira;
- IrInstGen *err_set_type;
- IrInstGen *payload_type;
- Buf *type_name;
-};
-
-struct ZigValue {
- ZigType *type;
- ConstValSpecial special;
- uint32_t llvm_align;
- ConstParent parent;
- LLVMValueRef llvm_value;
- LLVMValueRef llvm_global;
-
- union {
- // populated if special == ConstValSpecialLazy
- LazyValue *x_lazy;
-
- // populated if special == ConstValSpecialStatic
- BigInt x_bigint;
- BigFloat x_bigfloat;
- float16_t x_f16;
- float x_f32;
- double x_f64;
- float128_t x_f128;
- bool x_bool;
- ConstBoundFnValue x_bound_fn;
- ZigType *x_type;
- ZigValue *x_optional;
- ConstErrValue x_err_union;
- ErrorTableEntry *x_err_set;
- BigInt x_enum_tag;
- ConstStructValue x_struct;
- ConstUnionValue x_union;
- ConstArrayValue x_array;
- ConstPtrValue x_ptr;
- ConstArgTuple x_arg_tuple;
- Buf *x_enum_literal;
-
- // populated if special == ConstValSpecialRuntime
- RuntimeHintErrorUnion rh_error_union;
- RuntimeHintOptional rh_maybe;
- RuntimeHintPtr rh_ptr;
- RuntimeHintSlice rh_slice;
- } data;
-
- // uncomment this to find bugs. can't leave it uncommented because of a gcc-9 warning
- //ZigValue& operator= (const ZigValue &other) = delete; // use copy_const_val
-
- ZigValue(const ZigValue &other) = delete; // plz zero initialize with ZigValue val = {};
-
- // for use in debuggers
- void dump();
-};
-
-enum ReturnKnowledge {
- ReturnKnowledgeUnknown,
- ReturnKnowledgeKnownError,
- ReturnKnowledgeKnownNonError,
- ReturnKnowledgeKnownNull,
- ReturnKnowledgeKnownNonNull,
- ReturnKnowledgeSkipDefers,
-};
-
-enum VisibMod {
- VisibModPrivate,
- VisibModPub,
-};
-
-enum GlobalLinkageId {
- GlobalLinkageIdInternal,
- GlobalLinkageIdStrong,
- GlobalLinkageIdWeak,
- GlobalLinkageIdLinkOnce,
-};
-
-enum TldId {
- TldIdVar,
- TldIdFn,
- TldIdContainer,
- TldIdCompTime,
- TldIdUsingNamespace,
-};
-
-enum TldResolution {
- TldResolutionUnresolved,
- TldResolutionResolving,
- TldResolutionInvalid,
- TldResolutionOkLazy,
- TldResolutionOk,
-};
-
-struct Tld {
- TldId id;
- Buf *name;
- VisibMod visib_mod;
- AstNode *source_node;
-
- ZigType *import;
- Scope *parent_scope;
- TldResolution resolution;
-};
-
-struct TldVar {
- Tld base;
-
- ZigVar *var;
- Buf *extern_lib_name;
- bool analyzing_type; // flag to detect dependency loops
-};
-
-struct TldFn {
- Tld base;
-
- ZigFn *fn_entry;
- Buf *extern_lib_name;
-};
-
-struct TldContainer {
- Tld base;
-
- ScopeDecls *decls_scope;
- ZigType *type_entry;
-};
-
-struct TldCompTime {
- Tld base;
-};
-
-struct TldUsingNamespace {
- Tld base;
-
- ZigValue *using_namespace_value;
-};
-
-struct TypeEnumField {
- Buf *name;
- BigInt value;
- uint32_t decl_index;
- AstNode *decl_node;
-};
-
-struct TypeUnionField {
- Buf *name;
- ZigType *type_entry; // available after ResolveStatusSizeKnown
- ZigValue *type_val; // available after ResolveStatusZeroBitsKnown
- TypeEnumField *enum_field;
- AstNode *decl_node;
- uint32_t gen_index;
- uint32_t align;
-};
-
-enum NodeType {
- NodeTypeFnProto,
- NodeTypeFnDef,
- NodeTypeParamDecl,
- NodeTypeBlock,
- NodeTypeGroupedExpr,
- NodeTypeReturnExpr,
- NodeTypeDefer,
- NodeTypeVariableDeclaration,
- NodeTypeTestDecl,
- NodeTypeBinOpExpr,
- NodeTypeCatchExpr,
- NodeTypeFloatLiteral,
- NodeTypeIntLiteral,
- NodeTypeStringLiteral,
- NodeTypeCharLiteral,
- NodeTypeSymbol,
- NodeTypePrefixOpExpr,
- NodeTypePointerType,
- NodeTypeFnCallExpr,
- NodeTypeArrayAccessExpr,
- NodeTypeSliceExpr,
- NodeTypeFieldAccessExpr,
- NodeTypePtrDeref,
- NodeTypeUnwrapOptional,
- NodeTypeUsingNamespace,
- NodeTypeBoolLiteral,
- NodeTypeNullLiteral,
- NodeTypeUndefinedLiteral,
- NodeTypeUnreachable,
- NodeTypeIfBoolExpr,
- NodeTypeWhileExpr,
- NodeTypeForExpr,
- NodeTypeSwitchExpr,
- NodeTypeSwitchProng,
- NodeTypeSwitchRange,
- NodeTypeCompTime,
- NodeTypeNoSuspend,
- NodeTypeBreak,
- NodeTypeContinue,
- NodeTypeAsmExpr,
- NodeTypeContainerDecl,
- NodeTypeStructField,
- NodeTypeContainerInitExpr,
- NodeTypeStructValueField,
- NodeTypeArrayType,
- NodeTypeInferredArrayType,
- NodeTypeErrorType,
- NodeTypeIfErrorExpr,
- NodeTypeIfOptional,
- NodeTypeErrorSetDecl,
- NodeTypeErrorSetField,
- NodeTypeResume,
- NodeTypeAwaitExpr,
- NodeTypeSuspend,
- NodeTypeAnyFrameType,
- NodeTypeEnumLiteral,
- NodeTypeAnyTypeField,
-};
-
-enum FnInline {
- FnInlineAuto,
- FnInlineAlways,
- FnInlineNever,
-};
-
-struct AstNodeFnProto {
- Buf *name;
- ZigList params;
- AstNode *return_type;
- Token *return_anytype_token;
- AstNode *fn_def_node;
- // populated if this is an extern declaration
- Buf *lib_name;
- // populated if the "align A" is present
- AstNode *align_expr;
- // populated if the "section(S)" is present
- AstNode *section_expr;
- // populated if the "callconv(S)" is present
- AstNode *callconv_expr;
- Buf doc_comments;
-
- FnInline fn_inline;
-
- VisibMod visib_mod;
- bool auto_err_set;
- bool is_var_args;
- bool is_extern;
- bool is_export;
-};
-
-struct AstNodeFnDef {
- AstNode *fn_proto;
- AstNode *body;
-};
-
-struct AstNodeParamDecl {
- Buf *name;
- AstNode *type;
- Token *anytype_token;
- Buf doc_comments;
- bool is_noalias;
- bool is_comptime;
- bool is_var_args;
-};
-
-struct AstNodeBlock {
- Buf *name;
- ZigList statements;
-};
-
-enum ReturnKind {
- ReturnKindUnconditional,
- ReturnKindError,
-};
-
-struct AstNodeReturnExpr {
- ReturnKind kind;
- // might be null in case of return void;
- AstNode *expr;
-};
-
-struct AstNodeDefer {
- ReturnKind kind;
- AstNode *err_payload;
- AstNode *expr;
-
- // temporary data used in IR generation
- Scope *child_scope;
- Scope *expr_scope;
-};
-
-struct AstNodeVariableDeclaration {
- Buf *symbol;
- // one or both of type and expr will be non null
- AstNode *type;
- AstNode *expr;
- // populated if this is an extern declaration
- Buf *lib_name;
- // populated if the "align(A)" is present
- AstNode *align_expr;
- // populated if the "section(S)" is present
- AstNode *section_expr;
- Token *threadlocal_tok;
- Buf doc_comments;
-
- VisibMod visib_mod;
- bool is_const;
- bool is_comptime;
- bool is_export;
- bool is_extern;
-};
-
-struct AstNodeTestDecl {
- Buf *name;
-
- AstNode *body;
-};
-
-enum BinOpType {
- BinOpTypeInvalid,
- BinOpTypeAssign,
- BinOpTypeAssignTimes,
- BinOpTypeAssignTimesWrap,
- BinOpTypeAssignDiv,
- BinOpTypeAssignMod,
- BinOpTypeAssignPlus,
- BinOpTypeAssignPlusWrap,
- BinOpTypeAssignMinus,
- BinOpTypeAssignMinusWrap,
- BinOpTypeAssignBitShiftLeft,
- BinOpTypeAssignBitShiftRight,
- BinOpTypeAssignBitAnd,
- BinOpTypeAssignBitXor,
- BinOpTypeAssignBitOr,
- BinOpTypeAssignMergeErrorSets,
- BinOpTypeBoolOr,
- BinOpTypeBoolAnd,
- BinOpTypeCmpEq,
- BinOpTypeCmpNotEq,
- BinOpTypeCmpLessThan,
- BinOpTypeCmpGreaterThan,
- BinOpTypeCmpLessOrEq,
- BinOpTypeCmpGreaterOrEq,
- BinOpTypeBinOr,
- BinOpTypeBinXor,
- BinOpTypeBinAnd,
- BinOpTypeBitShiftLeft,
- BinOpTypeBitShiftRight,
- BinOpTypeAdd,
- BinOpTypeAddWrap,
- BinOpTypeSub,
- BinOpTypeSubWrap,
- BinOpTypeMult,
- BinOpTypeMultWrap,
- BinOpTypeDiv,
- BinOpTypeMod,
- BinOpTypeUnwrapOptional,
- BinOpTypeArrayCat,
- BinOpTypeArrayMult,
- BinOpTypeErrorUnion,
- BinOpTypeMergeErrorSets,
-};
-
-struct AstNodeBinOpExpr {
- AstNode *op1;
- BinOpType bin_op;
- AstNode *op2;
-};
-
-struct AstNodeCatchExpr {
- AstNode *op1;
- AstNode *symbol; // can be null
- AstNode *op2;
-};
-
-struct AstNodeUnwrapOptional {
- AstNode *expr;
-};
-
-// Must be synchronized with std.builtin.CallOptions.Modifier
-enum CallModifier {
- CallModifierNone,
- CallModifierAsync,
- CallModifierNeverTail,
- CallModifierNeverInline,
- CallModifierNoSuspend,
- CallModifierAlwaysTail,
- CallModifierAlwaysInline,
- CallModifierCompileTime,
-
- // These are additional tags in the compiler, but not exposed in the std lib.
- CallModifierBuiltin,
-};
-
-struct AstNodeFnCallExpr {
- AstNode *fn_ref_expr;
- ZigList params;
- CallModifier modifier;
- bool seen; // used by @compileLog
-};
-
-struct AstNodeArrayAccessExpr {
- AstNode *array_ref_expr;
- AstNode *subscript;
-};
-
-struct AstNodeSliceExpr {
- AstNode *array_ref_expr;
- AstNode *start;
- AstNode *end;
- AstNode *sentinel; // can be null
-};
-
-struct AstNodeFieldAccessExpr {
- AstNode *struct_expr;
- Buf *field_name;
-};
-
-struct AstNodePtrDerefExpr {
- AstNode *target;
-};
-
-enum PrefixOp {
- PrefixOpInvalid,
- PrefixOpBoolNot,
- PrefixOpBinNot,
- PrefixOpNegation,
- PrefixOpNegationWrap,
- PrefixOpOptional,
- PrefixOpAddrOf,
-};
-
-struct AstNodePrefixOpExpr {
- PrefixOp prefix_op;
- AstNode *primary_expr;
-};
-
-struct AstNodePointerType {
- Token *star_token;
- AstNode *sentinel;
- AstNode *align_expr;
- BigInt *bit_offset_start;
- BigInt *host_int_bytes;
- AstNode *op_expr;
- Token *allow_zero_token;
- bool is_const;
- bool is_volatile;
-};
-
-struct AstNodeInferredArrayType {
- AstNode *sentinel; // can be null
- AstNode *child_type;
-};
-
-struct AstNodeArrayType {
- AstNode *size;
- AstNode *sentinel;
- AstNode *child_type;
- AstNode *align_expr;
- Token *allow_zero_token;
- bool is_const;
- bool is_volatile;
-};
-
-struct AstNodeUsingNamespace {
- VisibMod visib_mod;
- AstNode *expr;
-};
-
-struct AstNodeIfBoolExpr {
- AstNode *condition;
- AstNode *then_block;
- AstNode *else_node; // null, block node, or other if expr node
-};
-
-struct AstNodeTryExpr {
- Buf *var_symbol;
- bool var_is_ptr;
- AstNode *target_node;
- AstNode *then_node;
- AstNode *else_node;
- Buf *err_symbol;
-};
-
-struct AstNodeTestExpr {
- Buf *var_symbol;
- bool var_is_ptr;
- AstNode *target_node;
- AstNode *then_node;
- AstNode *else_node; // null, block node, or other if expr node
-};
-
-struct AstNodeWhileExpr {
- Buf *name;
- AstNode *condition;
- Buf *var_symbol;
- bool var_is_ptr;
- AstNode *continue_expr;
- AstNode *body;
- AstNode *else_node;
- Buf *err_symbol;
- bool is_inline;
-};
-
-struct AstNodeForExpr {
- Buf *name;
- AstNode *array_expr;
- AstNode *elem_node; // always a symbol
- AstNode *index_node; // always a symbol, might be null
- AstNode *body;
- AstNode *else_node; // can be null
- bool elem_is_ptr;
- bool is_inline;
-};
-
-struct AstNodeSwitchExpr {
- AstNode *expr;
- ZigList prongs;
-};
-
-struct AstNodeSwitchProng {
- ZigList items;
- AstNode *var_symbol;
- AstNode *expr;
- bool var_is_ptr;
- bool any_items_are_range;
-};
-
-struct AstNodeSwitchRange {
- AstNode *start;
- AstNode *end;
-};
-
-struct AstNodeCompTime {
- AstNode *expr;
-};
-
-struct AstNodeNoSuspend {
- AstNode *expr;
-};
-
-struct AsmOutput {
- Buf *asm_symbolic_name;
- Buf *constraint;
- Buf *variable_name;
- AstNode *return_type; // null unless "=r" and return
-};
-
-struct AsmInput {
- Buf *asm_symbolic_name;
- Buf *constraint;
- AstNode *expr;
-};
-
-struct SrcPos {
- size_t line;
- size_t column;
-};
-
-enum AsmTokenId {
- AsmTokenIdTemplate,
- AsmTokenIdPercent,
- AsmTokenIdVar,
- AsmTokenIdUniqueId,
-};
-
-struct AsmToken {
- enum AsmTokenId id;
- size_t start;
- size_t end;
-};
-
-struct AstNodeAsmExpr {
- Token *volatile_token;
- AstNode *asm_template;
- ZigList output_list;
- ZigList input_list;
- ZigList clobber_list;
-};
-
-enum ContainerKind {
- ContainerKindStruct,
- ContainerKindEnum,
- ContainerKindUnion,
-};
-
-enum ContainerLayout {
- ContainerLayoutAuto,
- ContainerLayoutExtern,
- ContainerLayoutPacked,
-};
-
-struct AstNodeContainerDecl {
- AstNode *init_arg_expr; // enum(T), struct(endianness), or union(T), or union(enum(T))
- ZigList fields;
- ZigList decls;
- Buf doc_comments;
-
- ContainerKind kind;
- ContainerLayout layout;
-
- bool auto_enum, is_root; // union(enum)
-};
-
-struct AstNodeErrorSetField {
- Buf doc_comments;
- AstNode *field_name;
-};
-
-struct AstNodeErrorSetDecl {
- // Each AstNode could be AstNodeErrorSetField or just AstNodeSymbolExpr to save memory
- ZigList decls;
-};
-
-struct AstNodeStructField {
- Buf *name;
- AstNode *type;
- AstNode *value;
- // populated if the "align(A)" is present
- AstNode *align_expr;
- Buf doc_comments;
- Token *comptime_token;
-};
-
-struct AstNodeStringLiteral {
- Buf *buf;
-};
-
-struct AstNodeCharLiteral {
- uint32_t value;
-};
-
-struct AstNodeFloatLiteral {
- BigFloat *bigfloat;
-
- // overflow is true if when parsing the number, we discovered it would not
- // fit without losing data in a double
- bool overflow;
-};
-
-struct AstNodeIntLiteral {
- BigInt *bigint;
-};
-
-struct AstNodeStructValueField {
- Buf *name;
- AstNode *expr;
-};
-
-enum ContainerInitKind {
- ContainerInitKindStruct,
- ContainerInitKindArray,
-};
-
-struct AstNodeContainerInitExpr {
- AstNode *type;
- ZigList entries;
- ContainerInitKind kind;
-};
-
-struct AstNodeNullLiteral {
-};
-
-struct AstNodeUndefinedLiteral {
-};
-
-struct AstNodeThisLiteral {
-};
-
-struct AstNodeSymbolExpr {
- Buf *symbol;
-};
-
-struct AstNodeBoolLiteral {
- bool value;
-};
-
-struct AstNodeBreakExpr {
- Buf *name;
- AstNode *expr; // may be null
-};
-
-struct AstNodeResumeExpr {
- AstNode *expr;
-};
-
-struct AstNodeContinueExpr {
- Buf *name;
-};
-
-struct AstNodeUnreachableExpr {
-};
-
-
-struct AstNodeErrorType {
-};
-
-struct AstNodeAwaitExpr {
- AstNode *expr;
-};
-
-struct AstNodeSuspend {
- AstNode *block;
-};
-
-struct AstNodeAnyFrameType {
- AstNode *payload_type; // can be NULL
-};
-
-struct AstNodeEnumLiteral {
- Token *period;
- Token *identifier;
-};
-
-struct AstNode {
- enum NodeType type;
- bool already_traced_this_node;
- size_t line;
- size_t column;
- ZigType *owner;
- union {
- AstNodeFnDef fn_def;
- AstNodeFnProto fn_proto;
- AstNodeParamDecl param_decl;
- AstNodeBlock block;
- AstNode * grouped_expr;
- AstNodeReturnExpr return_expr;
- AstNodeDefer defer;
- AstNodeVariableDeclaration variable_declaration;
- AstNodeTestDecl test_decl;
- AstNodeBinOpExpr bin_op_expr;
- AstNodeCatchExpr unwrap_err_expr;
- AstNodeUnwrapOptional unwrap_optional;
- AstNodePrefixOpExpr prefix_op_expr;
- AstNodePointerType pointer_type;
- AstNodeFnCallExpr fn_call_expr;
- AstNodeArrayAccessExpr array_access_expr;
- AstNodeSliceExpr slice_expr;
- AstNodeUsingNamespace using_namespace;
- AstNodeIfBoolExpr if_bool_expr;
- AstNodeTryExpr if_err_expr;
- AstNodeTestExpr test_expr;
- AstNodeWhileExpr while_expr;
- AstNodeForExpr for_expr;
- AstNodeSwitchExpr switch_expr;
- AstNodeSwitchProng switch_prong;
- AstNodeSwitchRange switch_range;
- AstNodeCompTime comptime_expr;
- AstNodeNoSuspend nosuspend_expr;
- AstNodeAsmExpr asm_expr;
- AstNodeFieldAccessExpr field_access_expr;
- AstNodePtrDerefExpr ptr_deref_expr;
- AstNodeContainerDecl container_decl;
- AstNodeStructField struct_field;
- AstNodeStringLiteral string_literal;
- AstNodeCharLiteral char_literal;
- AstNodeFloatLiteral float_literal;
- AstNodeIntLiteral int_literal;
- AstNodeContainerInitExpr container_init_expr;
- AstNodeStructValueField struct_val_field;
- AstNodeNullLiteral null_literal;
- AstNodeUndefinedLiteral undefined_literal;
- AstNodeThisLiteral this_literal;
- AstNodeSymbolExpr symbol_expr;
- AstNodeBoolLiteral bool_literal;
- AstNodeBreakExpr break_expr;
- AstNodeContinueExpr continue_expr;
- AstNodeUnreachableExpr unreachable_expr;
- AstNodeArrayType array_type;
- AstNodeInferredArrayType inferred_array_type;
- AstNodeErrorType error_type;
- AstNodeErrorSetDecl err_set_decl;
- AstNodeErrorSetField err_set_field;
- AstNodeResumeExpr resume_expr;
- AstNodeAwaitExpr await_expr;
- AstNodeSuspend suspend;
- AstNodeAnyFrameType anyframe_type;
- AstNodeEnumLiteral enum_literal;
- } data;
-
- // This is a function for use in the debugger to print
- // the source location.
- void src();
-};
-
-// this struct is allocated with allocate_nonzero
-struct FnTypeParamInfo {
- bool is_noalias;
- ZigType *type;
-};
-
-struct GenericFnTypeId {
- CodeGen *codegen;
- ZigFn *fn_entry;
- ZigValue *params;
- size_t param_count;
-};
-
-uint32_t generic_fn_type_id_hash(GenericFnTypeId *id);
-bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b);
-
-struct FnTypeId {
- ZigType *return_type;
- FnTypeParamInfo *param_info;
- size_t param_count;
- size_t next_param_index;
- bool is_var_args;
- CallingConvention cc;
- uint32_t alignment;
-};
-
-uint32_t fn_type_id_hash(FnTypeId*);
-bool fn_type_id_eql(FnTypeId *a, FnTypeId *b);
-
-static const uint32_t VECTOR_INDEX_NONE = UINT32_MAX;
-static const uint32_t VECTOR_INDEX_RUNTIME = UINT32_MAX - 1;
-
-struct InferredStructField {
- ZigType *inferred_struct_type;
- Buf *field_name;
- bool already_resolved;
-};
-
-struct ZigTypePointer {
- ZigType *child_type;
- ZigType *slice_parent;
-
- // Anonymous struct literal syntax uses this when the result location has
- // no type in it. This field is null if this pointer does not refer to
- // a field of a currently-being-inferred struct type.
- // When this is non-null, the pointer is pointing to the base of the inferred
- // struct.
- InferredStructField *inferred_struct_field;
-
- // This can be null. If it is non-null, it means the pointer is terminated by this
- // sentinel value. This is most commonly used for C-style strings, with a 0 byte
- // to specify the length of the memory pointed to.
- ZigValue *sentinel;
-
- PtrLen ptr_len;
- uint32_t explicit_alignment; // 0 means use ABI alignment
-
- uint32_t bit_offset_in_host;
- // size of host integer. 0 means no host integer; this field is aligned
- // when vector_index != VECTOR_INDEX_NONE this is the len of the containing vector
- uint32_t host_int_bytes;
-
- uint32_t vector_index; // see the VECTOR_INDEX_* constants
- bool is_const;
- bool is_volatile;
- bool allow_zero;
- bool resolve_loop_flag_zero_bits;
-};
-
-struct ZigTypeInt {
- uint32_t bit_count;
- bool is_signed;
-};
-
-struct ZigTypeFloat {
- size_t bit_count;
-};
-
-// Needs to have the same memory layout as ZigTypeVector
-struct ZigTypeArray {
- ZigType *child_type;
- uint64_t len;
- ZigValue *sentinel;
-};
-
-struct TypeStructField {
- Buf *name;
- ZigType *type_entry; // available after ResolveStatusSizeKnown
- ZigValue *type_val; // available after ResolveStatusZeroBitsKnown
- size_t src_index;
- size_t gen_index;
- size_t offset; // byte offset from beginning of struct
- AstNode *decl_node;
- ZigValue *init_val; // null and then memoized
- uint32_t bit_offset_in_host; // offset from the memory at gen_index
- uint32_t host_int_bytes; // size of host integer
- uint32_t align;
- bool is_comptime;
-};
-
-enum ResolveStatus {
- ResolveStatusUnstarted,
- ResolveStatusInvalid,
- ResolveStatusBeingInferred,
- ResolveStatusZeroBitsKnown,
- ResolveStatusAlignmentKnown,
- ResolveStatusSizeKnown,
- ResolveStatusLLVMFwdDecl,
- ResolveStatusLLVMFull,
-};
-
-struct ZigPackage {
- Buf root_src_dir;
- Buf root_src_path; // relative to root_src_dir
- Buf pkg_path; // a.b.c.d which follows the package dependency chain from the root package
-
- // reminder: hash tables must be initialized before use
- HashMap package_table;
-
- bool added_to_cache;
-};
-
-// Stuff that only applies to a struct which is the implicit root struct of a file
-struct RootStruct {
- ZigPackage *package;
- Buf *path; // relative to root_package->root_src_dir
- ZigList *line_offsets;
- Buf *source_code;
- ZigLLVMDIFile *di_file;
-};
-
-enum StructSpecial {
- StructSpecialNone,
- StructSpecialSlice,
- StructSpecialInferredTuple,
- StructSpecialInferredStruct,
-};
-
-struct ZigTypeStruct {
- AstNode *decl_node;
- TypeStructField **fields;
- ScopeDecls *decls_scope;
- HashMap fields_by_name;
- RootStruct *root_struct;
- uint32_t *host_int_bytes; // available for packed structs, indexed by gen_index
- size_t llvm_full_type_queue_index;
-
- uint32_t src_field_count;
- uint32_t gen_field_count;
-
- ContainerLayout layout;
- ResolveStatus resolve_status;
-
- StructSpecial special;
- // whether any of the fields require comptime
- // known after ResolveStatusZeroBitsKnown
- bool requires_comptime;
- bool resolve_loop_flag_zero_bits;
- bool resolve_loop_flag_other;
- bool created_by_at_type;
-};
-
-struct ZigTypeOptional {
- ZigType *child_type;
- ResolveStatus resolve_status;
-};
-
-struct ZigTypeErrorUnion {
- ZigType *err_set_type;
- ZigType *payload_type;
- size_t pad_bytes;
- LLVMTypeRef pad_llvm_type;
-};
-
-struct ZigTypeErrorSet {
- ErrorTableEntry **errors;
- ZigFn *infer_fn;
- uint32_t err_count;
- bool incomplete;
-};
-
-struct ZigTypeEnum {
- AstNode *decl_node;
- TypeEnumField *fields;
- ZigType *tag_int_type;
-
- ScopeDecls *decls_scope;
-
- LLVMValueRef name_function;
-
- HashMap fields_by_name;
- uint32_t src_field_count;
-
- ContainerLayout layout;
- ResolveStatus resolve_status;
-
- bool non_exhaustive;
- bool resolve_loop_flag;
-};
-
-uint32_t type_ptr_hash(const ZigType *ptr);
-bool type_ptr_eql(const ZigType *a, const ZigType *b);
-
-uint32_t pkg_ptr_hash(const ZigPackage *ptr);
-bool pkg_ptr_eql(const ZigPackage *a, const ZigPackage *b);
-
-uint32_t tld_ptr_hash(const Tld *ptr);
-bool tld_ptr_eql(const Tld *a, const Tld *b);
-
-uint32_t node_ptr_hash(const AstNode *ptr);
-bool node_ptr_eql(const AstNode *a, const AstNode *b);
-
-uint32_t fn_ptr_hash(const ZigFn *ptr);
-bool fn_ptr_eql(const ZigFn *a, const ZigFn *b);
-
-uint32_t err_ptr_hash(const ErrorTableEntry *ptr);
-bool err_ptr_eql(const ErrorTableEntry *a, const ErrorTableEntry *b);
-
-struct ZigTypeUnion {
- AstNode *decl_node;
- TypeUnionField *fields;
- ScopeDecls *decls_scope;
- HashMap fields_by_name;
- ZigType *tag_type; // always an enum or null
- LLVMTypeRef union_llvm_type;
- TypeUnionField *most_aligned_union_member;
- size_t gen_union_index;
- size_t gen_tag_index;
- size_t union_abi_size;
-
- uint32_t src_field_count;
- uint32_t gen_field_count;
-
- ContainerLayout layout;
- ResolveStatus resolve_status;
-
- bool have_explicit_tag_type;
- // whether any of the fields require comptime
- // the value is not valid until zero_bits_known == true
- bool requires_comptime;
- bool resolve_loop_flag_zero_bits;
- bool resolve_loop_flag_other;
-};
-
-struct FnGenParamInfo {
- size_t src_index;
- size_t gen_index;
- bool is_byval;
- ZigType *type;
-};
-
-struct ZigTypeFn {
- FnTypeId fn_type_id;
- bool is_generic;
- ZigType *gen_return_type;
- size_t gen_param_count;
- FnGenParamInfo *gen_param_info;
-
- LLVMTypeRef raw_type_ref;
- ZigLLVMDIType *raw_di_type;
-
- ZigType *bound_fn_parent;
-};
-
-struct ZigTypeBoundFn {
- ZigType *fn_type;
-};
-
-// Needs to have the same memory layout as ZigTypeArray
-struct ZigTypeVector {
- // The type must be a pointer, integer, bool, or float
- ZigType *elem_type;
- uint64_t len;
- size_t padding;
-};
-
-// A lot of code is relying on ZigTypeArray and ZigTypeVector having the same layout/size
-static_assert(sizeof(ZigTypeVector) == sizeof(ZigTypeArray), "Size of ZigTypeVector and ZigTypeArray do not match!");
-
-enum ZigTypeId {
- ZigTypeIdInvalid,
- ZigTypeIdMetaType,
- ZigTypeIdVoid,
- ZigTypeIdBool,
- ZigTypeIdUnreachable,
- ZigTypeIdInt,
- ZigTypeIdFloat,
- ZigTypeIdPointer,
- ZigTypeIdArray,
- ZigTypeIdStruct,
- ZigTypeIdComptimeFloat,
- ZigTypeIdComptimeInt,
- ZigTypeIdUndefined,
- ZigTypeIdNull,
- ZigTypeIdOptional,
- ZigTypeIdErrorUnion,
- ZigTypeIdErrorSet,
- ZigTypeIdEnum,
- ZigTypeIdUnion,
- ZigTypeIdFn,
- ZigTypeIdBoundFn,
- ZigTypeIdOpaque,
- ZigTypeIdFnFrame,
- ZigTypeIdAnyFrame,
- ZigTypeIdVector,
- ZigTypeIdEnumLiteral,
-};
-
-enum OnePossibleValue {
- OnePossibleValueInvalid,
- OnePossibleValueNo,
- OnePossibleValueYes,
-};
-
-struct ZigTypeOpaque {
- Buf *bare_name;
-};
-
-struct ZigTypeFnFrame {
- ZigFn *fn;
- ZigType *locals_struct;
-
- // This is set to the type that resolving the frame currently depends on, null if none.
- // It's for generating a helpful error message.
- ZigType *resolve_loop_type;
- AstNode *resolve_loop_src_node;
- bool reported_loop_err;
-};
-
-struct ZigTypeAnyFrame {
- ZigType *result_type; // null if `anyframe` instead of `anyframe->T`
-};
-
-struct ZigType {
- ZigTypeId id;
- Buf name;
-
- // These are not supposed to be accessed directly. They're
- // null during semantic analysis, memoized with get_llvm_type
- // and get_llvm_di_type
- LLVMTypeRef llvm_type;
- ZigLLVMDIType *llvm_di_type;
-
- union {
- ZigTypePointer pointer;
- ZigTypeInt integral;
- ZigTypeFloat floating;
- ZigTypeArray array;
- ZigTypeStruct structure;
- ZigTypeOptional maybe;
- ZigTypeErrorUnion error_union;
- ZigTypeErrorSet error_set;
- ZigTypeEnum enumeration;
- ZigTypeUnion unionation;
- ZigTypeFn fn;
- ZigTypeBoundFn bound_fn;
- ZigTypeVector vector;
- ZigTypeOpaque opaque;
- ZigTypeFnFrame frame;
- ZigTypeAnyFrame any_frame;
- } data;
-
- // use these fields to make sure we don't duplicate type table entries for the same type
- ZigType *pointer_parent[2]; // [0 - mut, 1 - const]
- ZigType *optional_parent;
- ZigType *any_frame_parent;
- // If we generate a constant name value for this type, we memoize it here.
- // The type of this is array
- ZigValue *cached_const_name_val;
-
- OnePossibleValue one_possible_value;
- // Known after ResolveStatusAlignmentKnown.
- uint32_t abi_align;
- // The offset in bytes between consecutive array elements of this type. Known
- // after ResolveStatusSizeKnown.
- 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 {
- FnAnalStateReady,
- FnAnalStateProbing,
- FnAnalStateComplete,
- FnAnalStateInvalid,
-};
-
-struct GlobalExport {
- Buf name;
- GlobalLinkageId linkage;
-};
-
-struct ZigFn {
- LLVMValueRef llvm_value;
- const char *llvm_name;
- AstNode *proto_node;
- AstNode *body_node;
- ScopeFnDef *fndef_scope; // parent should be the top level decls or container decls
- Scope *child_scope; // parent is scope for last parameter
- ScopeBlock *def_scope; // parent is child_scope
- Buf symbol_name;
- // This is the function type assuming the function does not suspend.
- // Note that for an async function, this can be shared with non-async functions. So the value here
- // should only be read for things in common between non-async and async function types.
- ZigType *type_entry;
- // For normal functions one could use the type_entry->raw_type_ref and type_entry->raw_di_type.
- // However for functions that suspend, those values could possibly be their non-suspending equivalents.
- // So these values should be preferred.
- LLVMTypeRef raw_type_ref;
- ZigLLVMDIType *raw_di_type;
-
- ZigType *frame_type;
- // in the case of normal functions this is the implicit return type
- // in the case of async functions this is the implicit return type according to the
- // zig source code, not according to zig ir
- ZigType *src_implicit_return_type;
- IrExecutableSrc *ir_executable;
- IrExecutableGen analyzed_executable;
- size_t prealloc_bbc;
- size_t prealloc_backward_branch_quota;
- AstNode **param_source_nodes;
- Buf **param_names;
- IrInstGen *err_code_spill;
- AstNode *assumed_non_async;
-
- AstNode *fn_no_inline_set_node;
- AstNode *fn_static_eval_set_node;
-
- ZigList alloca_gen_list;
- ZigList variable_list;
-
- Buf *section_name;
- AstNode *set_alignstack_node;
-
- AstNode *set_cold_node;
- const AstNode *inferred_async_node;
- ZigFn *inferred_async_fn;
- AstNode *non_async_node;
-
- ZigList export_list;
- ZigList call_list;
- ZigList await_list;
-
- LLVMValueRef valgrind_client_request_array;
-
- FnInline fn_inline;
- FnAnalState anal_state;
-
- uint32_t align_bytes;
- uint32_t alignstack_value;
-
- bool calls_or_awaits_errorable_fn;
- bool is_cold;
- bool is_test;
-};
-
-uint32_t fn_table_entry_hash(ZigFn*);
-bool fn_table_entry_eql(ZigFn *a, ZigFn *b);
-
-enum BuiltinFnId {
- BuiltinFnIdInvalid,
- BuiltinFnIdMemcpy,
- BuiltinFnIdMemset,
- BuiltinFnIdSizeof,
- BuiltinFnIdAlignOf,
- BuiltinFnIdField,
- BuiltinFnIdTypeInfo,
- BuiltinFnIdType,
- BuiltinFnIdHasField,
- BuiltinFnIdTypeof,
- BuiltinFnIdAddWithOverflow,
- BuiltinFnIdSubWithOverflow,
- BuiltinFnIdMulWithOverflow,
- BuiltinFnIdShlWithOverflow,
- BuiltinFnIdMulAdd,
- BuiltinFnIdCInclude,
- BuiltinFnIdCDefine,
- BuiltinFnIdCUndef,
- BuiltinFnIdCompileErr,
- BuiltinFnIdCompileLog,
- BuiltinFnIdCtz,
- BuiltinFnIdClz,
- BuiltinFnIdPopCount,
- BuiltinFnIdBswap,
- BuiltinFnIdBitReverse,
- BuiltinFnIdImport,
- BuiltinFnIdCImport,
- BuiltinFnIdErrName,
- BuiltinFnIdBreakpoint,
- BuiltinFnIdReturnAddress,
- BuiltinFnIdEmbedFile,
- BuiltinFnIdCmpxchgWeak,
- BuiltinFnIdCmpxchgStrong,
- BuiltinFnIdFence,
- BuiltinFnIdDivExact,
- BuiltinFnIdDivTrunc,
- BuiltinFnIdDivFloor,
- BuiltinFnIdRem,
- BuiltinFnIdMod,
- BuiltinFnIdSqrt,
- BuiltinFnIdSin,
- BuiltinFnIdCos,
- BuiltinFnIdExp,
- BuiltinFnIdExp2,
- BuiltinFnIdLog,
- BuiltinFnIdLog2,
- BuiltinFnIdLog10,
- BuiltinFnIdFabs,
- BuiltinFnIdFloor,
- BuiltinFnIdCeil,
- BuiltinFnIdTrunc,
- BuiltinFnIdNearbyInt,
- BuiltinFnIdRound,
- BuiltinFnIdTruncate,
- BuiltinFnIdIntCast,
- BuiltinFnIdFloatCast,
- BuiltinFnIdErrSetCast,
- BuiltinFnIdIntToFloat,
- BuiltinFnIdFloatToInt,
- BuiltinFnIdBoolToInt,
- BuiltinFnIdErrToInt,
- BuiltinFnIdIntToErr,
- BuiltinFnIdEnumToInt,
- BuiltinFnIdIntToEnum,
- BuiltinFnIdVectorType,
- BuiltinFnIdShuffle,
- BuiltinFnIdSplat,
- BuiltinFnIdSetCold,
- BuiltinFnIdSetRuntimeSafety,
- BuiltinFnIdSetFloatMode,
- BuiltinFnIdTypeName,
- BuiltinFnIdPanic,
- BuiltinFnIdPtrCast,
- BuiltinFnIdBitCast,
- BuiltinFnIdIntToPtr,
- BuiltinFnIdPtrToInt,
- BuiltinFnIdTagName,
- BuiltinFnIdTagType,
- BuiltinFnIdFieldParentPtr,
- BuiltinFnIdByteOffsetOf,
- BuiltinFnIdBitOffsetOf,
- BuiltinFnIdAsyncCall,
- BuiltinFnIdShlExact,
- BuiltinFnIdShrExact,
- BuiltinFnIdSetEvalBranchQuota,
- BuiltinFnIdAlignCast,
- BuiltinFnIdThis,
- BuiltinFnIdSetAlignStack,
- BuiltinFnIdExport,
- BuiltinFnIdErrorReturnTrace,
- BuiltinFnIdAtomicRmw,
- BuiltinFnIdAtomicLoad,
- BuiltinFnIdAtomicStore,
- BuiltinFnIdHasDecl,
- BuiltinFnIdUnionInit,
- BuiltinFnIdFrameAddress,
- BuiltinFnIdFrameType,
- BuiltinFnIdFrameHandle,
- BuiltinFnIdFrameSize,
- BuiltinFnIdAs,
- BuiltinFnIdCall,
- BuiltinFnIdBitSizeof,
- BuiltinFnIdWasmMemorySize,
- BuiltinFnIdWasmMemoryGrow,
- BuiltinFnIdSrc,
-};
-
-struct BuiltinFnEntry {
- BuiltinFnId id;
- Buf name;
- size_t param_count;
-};
-
-enum PanicMsgId {
- PanicMsgIdUnreachable,
- PanicMsgIdBoundsCheckFailure,
- PanicMsgIdCastNegativeToUnsigned,
- PanicMsgIdCastTruncatedData,
- PanicMsgIdIntegerOverflow,
- PanicMsgIdShlOverflowedBits,
- PanicMsgIdShrOverflowedBits,
- PanicMsgIdDivisionByZero,
- PanicMsgIdRemainderDivisionByZero,
- PanicMsgIdExactDivisionRemainder,
- PanicMsgIdUnwrapOptionalFail,
- PanicMsgIdInvalidErrorCode,
- PanicMsgIdIncorrectAlignment,
- PanicMsgIdBadUnionField,
- PanicMsgIdBadEnumValue,
- PanicMsgIdFloatToInt,
- PanicMsgIdPtrCastNull,
- PanicMsgIdBadResume,
- PanicMsgIdBadAwait,
- PanicMsgIdBadReturn,
- PanicMsgIdResumedAnAwaitingFn,
- PanicMsgIdFrameTooSmall,
- PanicMsgIdResumedFnPendingAwait,
- PanicMsgIdBadNoSuspendCall,
- PanicMsgIdResumeNotSuspendedFn,
- PanicMsgIdBadSentinel,
- PanicMsgIdShxTooBigRhs,
-
- PanicMsgIdCount,
-};
-
-uint32_t fn_eval_hash(Scope*);
-bool fn_eval_eql(Scope *a, Scope *b);
-
-struct TypeId {
- ZigTypeId id;
-
- union {
- struct {
- CodeGen *codegen;
- ZigType *child_type;
- InferredStructField *inferred_struct_field;
- ZigValue *sentinel;
- PtrLen ptr_len;
- uint32_t alignment;
-
- uint32_t bit_offset_in_host;
- uint32_t host_int_bytes;
-
- uint32_t vector_index;
- bool is_const;
- bool is_volatile;
- bool allow_zero;
- } pointer;
- struct {
- CodeGen *codegen;
- ZigType *child_type;
- uint64_t size;
- ZigValue *sentinel;
- } array;
- struct {
- bool is_signed;
- uint32_t bit_count;
- } integer;
- struct {
- ZigType *err_set_type;
- ZigType *payload_type;
- } error_union;
- struct {
- ZigType *elem_type;
- uint32_t len;
- } vector;
- } data;
-};
-
-uint32_t type_id_hash(TypeId);
-bool type_id_eql(TypeId a, TypeId b);
-
-enum ZigLLVMFnId {
- ZigLLVMFnIdCtz,
- ZigLLVMFnIdClz,
- ZigLLVMFnIdPopCount,
- ZigLLVMFnIdOverflowArithmetic,
- ZigLLVMFnIdFMA,
- ZigLLVMFnIdFloatOp,
- ZigLLVMFnIdBswap,
- ZigLLVMFnIdBitReverse,
-};
-
-// There are a bunch of places in code that rely on these values being in
-// exactly this order.
-enum AddSubMul {
- AddSubMulAdd = 0,
- AddSubMulSub = 1,
- AddSubMulMul = 2,
-};
-
-struct ZigLLVMFnKey {
- ZigLLVMFnId id;
-
- union {
- struct {
- uint32_t bit_count;
- } ctz;
- struct {
- uint32_t bit_count;
- } clz;
- struct {
- uint32_t bit_count;
- } pop_count;
- struct {
- BuiltinFnId op;
- uint32_t bit_count;
- uint32_t vector_len; // 0 means not a vector
- } floating;
- struct {
- AddSubMul add_sub_mul;
- uint32_t bit_count;
- uint32_t vector_len; // 0 means not a vector
- bool is_signed;
- } overflow_arithmetic;
- struct {
- uint32_t bit_count;
- uint32_t vector_len; // 0 means not a vector
- } bswap;
- struct {
- uint32_t bit_count;
- } bit_reverse;
- } data;
-};
-
-uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey);
-bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b);
-
-struct TimeEvent {
- double time;
- 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 {
- // arena allocator destroyed just prior to codegen emit
- heap::ArenaAllocator *pass1_arena;
-
- //////////////////////////// Runtime State
- LLVMModuleRef module;
- ZigList errors;
- ErrorMsg *trace_err;
- LLVMBuilderRef builder;
- 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;
- LLVMValueRef cur_ret_ptr;
- LLVMValueRef cur_frame_ptr;
- LLVMValueRef cur_fn_val;
- LLVMValueRef cur_async_switch_instr;
- LLVMValueRef cur_async_resume_index_ptr;
- LLVMValueRef cur_async_awaiter_ptr;
- LLVMBasicBlockRef cur_preamble_llvm_block;
- size_t cur_resume_block_count;
- LLVMValueRef cur_err_ret_trace_val_arg;
- LLVMValueRef cur_err_ret_trace_val_stack;
- LLVMValueRef cur_bad_not_suspended_index;
- LLVMValueRef memcpy_fn_val;
- LLVMValueRef memset_fn_val;
- LLVMValueRef trap_fn_val;
- LLVMValueRef return_address_fn_val;
- LLVMValueRef frame_address_fn_val;
- LLVMValueRef add_error_return_trace_addr_fn_val;
- LLVMValueRef stacksave_fn_val;
- LLVMValueRef stackrestore_fn_val;
- LLVMValueRef write_register_fn_val;
- LLVMValueRef merge_err_ret_traces_fn_val;
- LLVMValueRef sp_md_node;
- LLVMValueRef err_name_table;
- LLVMValueRef safety_crash_err_fn;
- LLVMValueRef return_err_fn;
- LLVMValueRef wasm_memory_size;
- LLVMValueRef wasm_memory_grow;
- LLVMTypeRef anyframe_fn_type;
-
- // reminder: hash tables must be initialized before use
- HashMap import_table;
- HashMap builtin_fn_table;
- HashMap primitive_type_table;
- HashMap type_table;
- HashMap fn_type_table;
- HashMap error_table;
- HashMap generic_table;
- HashMap memoized_fn_eval_table;
- HashMap llvm_fn_table;
- HashMap exported_symbol_names;
- HashMap external_symbol_names;
- HashMap string_literals_table;
- HashMap type_info_cache;
- HashMap one_possible_values;
-
- ZigList resolve_queue;
- size_t resolve_queue_index;
- ZigList timing_events;
- ZigList inline_fns;
- ZigList test_fns;
- ZigList errors_by_index;
- ZigList caches_to_release;
- size_t largest_err_name_len;
- ZigList type_resolve_stack;
-
- ZigPackage *std_package;
- ZigPackage *test_runner_package;
- ZigPackage *compile_var_package;
- ZigPackage *root_pkg; // @import("root")
- ZigPackage *main_pkg; // usually same as root_pkg, except for `zig test`
- ZigType *compile_var_import;
- ZigType *root_import;
- ZigType *start_import;
-
- struct {
- ZigType *entry_bool;
- ZigType *entry_c_int[CIntTypeCount];
- ZigType *entry_c_longdouble;
- ZigType *entry_c_void;
- ZigType *entry_u8;
- ZigType *entry_u16;
- ZigType *entry_u32;
- ZigType *entry_u29;
- ZigType *entry_u64;
- ZigType *entry_i8;
- ZigType *entry_i32;
- ZigType *entry_i64;
- ZigType *entry_isize;
- ZigType *entry_usize;
- ZigType *entry_f16;
- ZigType *entry_f32;
- ZigType *entry_f64;
- ZigType *entry_f128;
- ZigType *entry_void;
- ZigType *entry_unreachable;
- ZigType *entry_type;
- ZigType *entry_invalid;
- ZigType *entry_block;
- ZigType *entry_num_lit_int;
- ZigType *entry_num_lit_float;
- ZigType *entry_undef;
- ZigType *entry_null;
- ZigType *entry_anytype;
- ZigType *entry_global_error_set;
- ZigType *entry_enum_literal;
- ZigType *entry_any_frame;
- } builtin_types;
-
- struct Intern {
- ZigValue x_undefined;
- ZigValue x_void;
- ZigValue x_null;
- ZigValue x_unreachable;
- ZigValue zero_byte;
-
- ZigValue *for_undefined();
- ZigValue *for_void();
- ZigValue *for_null();
- ZigValue *for_unreachable();
- ZigValue *for_zero_byte();
- } intern;
-
- ZigType *align_amt_type;
- ZigType *stack_trace_type;
- ZigType *err_tag_type;
- ZigType *test_fn_type;
-
- Buf llvm_triple_str;
- Buf global_asm;
- Buf o_file_output_path;
- Buf bin_file_output_path;
- Buf asm_file_output_path;
- Buf llvm_ir_file_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 *zig_std_special_dir; // Cannot be overridden; derived from zig_lib_dir.
-
- IrInstSrc *invalid_inst_src;
- IrInstGen *invalid_inst_gen;
- IrInstGen *unreach_instruction;
-
- ZigValue panic_msg_vals[PanicMsgIdCount];
-
- // The function definitions this module includes.
- ZigList fn_defs;
- size_t fn_defs_index;
- ZigList global_vars;
-
- ZigFn *cur_fn;
- ZigFn *panic_fn;
-
- ZigFn *largest_frame_fn;
-
- 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;
-
- 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 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;
-
- 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 {
- const char *name;
- ZigValue *const_value;
- ZigType *var_type;
- LLVMValueRef value_ref;
- IrInstSrc *is_comptime;
- IrInstGen *ptr_instruction;
- // which node is the declaration of the variable
- AstNode *decl_node;
- ZigLLVMDILocalVariable *di_loc_var;
- size_t src_arg_index;
- Scope *parent_scope;
- Scope *child_scope;
- LLVMValueRef param_value_ref;
-
- Buf *section_name;
-
- // In an inline loop, multiple variables may be created,
- // In this case, a reference to a variable should follow
- // this pointer to the redefined variable.
- ZigVar *next_var;
-
- ZigList export_list;
-
- uint32_t align_bytes;
- uint32_t ref_count;
-
- bool shadowable;
- bool src_is_const;
- bool gen_is_const;
- bool is_thread_local;
- bool is_comptime_memoized;
- bool is_comptime_memoized_value;
- bool did_the_decl_codegen;
-};
-
-struct ErrorTableEntry {
- Buf name;
- uint32_t value;
- AstNode *decl_node;
- ErrorTableEntry *other; // null, or another error decl that was merged into this
- ZigType *set_with_only_this_in_it;
- // If we generate a constant error name value for this error, we memoize it here.
- // The type of this is array
- ZigValue *cached_error_name_val;
-};
-
-enum ScopeId {
- ScopeIdDecls,
- ScopeIdBlock,
- ScopeIdDefer,
- ScopeIdDeferExpr,
- ScopeIdVarDecl,
- ScopeIdCImport,
- ScopeIdLoop,
- ScopeIdSuspend,
- ScopeIdFnDef,
- ScopeIdCompTime,
- ScopeIdRuntime,
- ScopeIdTypeOf,
- ScopeIdExpr,
- ScopeIdNoSuspend,
-};
-
-struct Scope {
- CodeGen *codegen;
- AstNode *source_node;
-
- // if the scope has a parent, this is it
- Scope *parent;
-
- ZigLLVMDIScope *di_scope;
- ScopeId id;
-};
-
-// This scope comes from global declarations or from
-// declarations in a container declaration
-// NodeTypeContainerDecl
-struct ScopeDecls {
- Scope base;
-
- HashMap decl_table;
- ZigList use_decls;
- AstNode *safety_set_node;
- AstNode *fast_math_set_node;
- ZigType *import;
- // If this is a scope from a container, this is the type entry, otherwise null
- ZigType *container_type;
- Buf *bare_name;
-
- bool safety_off;
- bool fast_math_on;
- bool any_imports_failed;
-};
-
-enum LVal {
- LValNone,
- LValPtr,
- LValAssign,
-};
-
-// This scope comes from a block expression in user code.
-// NodeTypeBlock
-struct ScopeBlock {
- Scope base;
-
- Buf *name;
- IrBasicBlockSrc *end_block;
- IrInstSrc *is_comptime;
- ResultLocPeerParent *peer_parent;
- ZigList *incoming_values;
- ZigList *incoming_blocks;
-
- AstNode *safety_set_node;
- AstNode *fast_math_set_node;
-
- LVal lval;
- bool safety_off;
- bool fast_math_on;
- bool name_used;
-};
-
-// This scope is created from every defer expression.
-// It's the code following the defer statement.
-// NodeTypeDefer
-struct ScopeDefer {
- Scope base;
-};
-
-// This scope is created from every defer expression.
-// It's the parent of the defer expression itself.
-// NodeTypeDefer
-struct ScopeDeferExpr {
- Scope base;
-
- bool reported_err;
-};
-
-// This scope is created for every variable declaration inside an IrExecutable
-// NodeTypeVariableDeclaration, NodeTypeParamDecl
-struct ScopeVarDecl {
- Scope base;
-
- // The variable that creates this scope
- ZigVar *var;
-};
-
-// This scope is created for a @cImport
-// NodeTypeFnCallExpr
-struct ScopeCImport {
- Scope base;
-
- Buf buf;
-};
-
-// This scope is created for a loop such as for or while in order to
-// make break and continue statements work.
-// NodeTypeForExpr or NodeTypeWhileExpr
-struct ScopeLoop {
- Scope base;
-
- LVal lval;
- Buf *name;
- IrBasicBlockSrc *break_block;
- IrBasicBlockSrc *continue_block;
- IrInstSrc *is_comptime;
- ZigList *incoming_values;
- ZigList *incoming_blocks;
- ResultLocPeerParent *peer_parent;
- ScopeExpr *spill_scope;
-
- bool name_used;
-};
-
-// This scope blocks certain things from working such as comptime continue
-// inside a runtime if expression.
-// NodeTypeIfBoolExpr, NodeTypeWhileExpr, NodeTypeForExpr
-struct ScopeRuntime {
- Scope base;
-
- IrInstSrc *is_comptime;
-};
-
-// This scope is created for a suspend block in order to have labeled
-// suspend for breaking out of a suspend and for detecting if a suspend
-// block is inside a suspend block.
-struct ScopeSuspend {
- Scope base;
-
- bool reported_err;
-};
-
-// This scope is created for a comptime expression.
-// NodeTypeCompTime, NodeTypeSwitchExpr
-struct ScopeCompTime {
- Scope base;
-};
-
-// This scope is created for a nosuspend expression.
-// NodeTypeNoSuspend
-struct ScopeNoSuspend {
- Scope base;
-};
-
-// This scope is created for a function definition.
-// NodeTypeFnDef
-struct ScopeFnDef {
- Scope base;
-
- ZigFn *fn_entry;
-};
-
-// This scope is created for a @TypeOf.
-// All runtime side-effects are elided within it.
-// NodeTypeFnCallExpr
-struct ScopeTypeOf {
- Scope base;
-};
-
-enum MemoizedBool {
- MemoizedBoolUnknown,
- MemoizedBoolFalse,
- MemoizedBoolTrue,
-};
-
-// This scope is created for each expression.
-// It's used to identify when an instruction needs to be spilled,
-// so that it can be accessed after a suspend point.
-struct ScopeExpr {
- Scope base;
-
- ScopeExpr **children_ptr;
- size_t children_len;
-
- MemoizedBool need_spill;
- // This is a hack. I apologize for this, I need this to work so that I
- // can make progress on other fronts. I'll pay off this tech debt eventually.
- bool spill_harder;
-};
-
-// synchronized with code in define_builtin_compile_vars
-enum AtomicOrder {
- AtomicOrderUnordered,
- AtomicOrderMonotonic,
- AtomicOrderAcquire,
- AtomicOrderRelease,
- AtomicOrderAcqRel,
- AtomicOrderSeqCst,
-};
-
-// synchronized with the code in define_builtin_compile_vars
-enum AtomicRmwOp {
- AtomicRmwOp_xchg,
- AtomicRmwOp_add,
- AtomicRmwOp_sub,
- AtomicRmwOp_and,
- AtomicRmwOp_nand,
- AtomicRmwOp_or,
- AtomicRmwOp_xor,
- AtomicRmwOp_max,
- AtomicRmwOp_min,
-};
-
-// A basic block contains no branching. Branches send control flow
-// to another basic block.
-// Phi instructions must be first in a basic block.
-// The last instruction in a basic block must be of type unreachable.
-struct IrBasicBlockSrc {
- ZigList instruction_list;
- IrBasicBlockGen *child;
- Scope *scope;
- const char *name_hint;
- IrInst *suspend_instruction_ref;
-
- uint32_t ref_count;
- uint32_t index; // index into the basic block list
-
- uint32_t debug_id;
- bool suspended;
- bool in_resume_stack;
-};
-
-struct IrBasicBlockGen {
- ZigList instruction_list;
- Scope *scope;
- const char *name_hint;
- LLVMBasicBlockRef llvm_block;
- LLVMBasicBlockRef llvm_exit_block;
- // The instruction that referenced this basic block and caused us to
- // analyze the basic block. If the same instruction wants us to emit
- // the same basic block, then we re-generate it instead of saving it.
- IrInst *ref_instruction;
- // When this is non-null, a branch to this basic block is only allowed
- // if the branch is comptime. The instruction points to the reason
- // the basic block must be comptime.
- IrInst *must_be_comptime_source_instr;
-
- uint32_t debug_id;
- bool already_appended;
-};
-
-// Src instructions are generated by ir_gen_* functions in ir.cpp from AST.
-// ir_analyze_* functions consume Src instructions and produce Gen instructions.
-// Src instructions do not have type information; Gen instructions do.
-enum IrInstSrcId {
- IrInstSrcIdInvalid,
- IrInstSrcIdDeclVar,
- IrInstSrcIdBr,
- IrInstSrcIdCondBr,
- IrInstSrcIdSwitchBr,
- IrInstSrcIdSwitchVar,
- IrInstSrcIdSwitchElseVar,
- IrInstSrcIdSwitchTarget,
- IrInstSrcIdPhi,
- IrInstSrcIdUnOp,
- IrInstSrcIdBinOp,
- IrInstSrcIdMergeErrSets,
- IrInstSrcIdLoadPtr,
- IrInstSrcIdStorePtr,
- IrInstSrcIdFieldPtr,
- IrInstSrcIdElemPtr,
- IrInstSrcIdVarPtr,
- IrInstSrcIdCall,
- IrInstSrcIdCallArgs,
- IrInstSrcIdCallExtra,
- IrInstSrcIdAsyncCallExtra,
- IrInstSrcIdConst,
- IrInstSrcIdReturn,
- IrInstSrcIdContainerInitList,
- IrInstSrcIdContainerInitFields,
- IrInstSrcIdUnreachable,
- IrInstSrcIdTypeOf,
- IrInstSrcIdSetCold,
- IrInstSrcIdSetRuntimeSafety,
- IrInstSrcIdSetFloatMode,
- IrInstSrcIdArrayType,
- IrInstSrcIdAnyFrameType,
- IrInstSrcIdSliceType,
- IrInstSrcIdAsm,
- IrInstSrcIdSizeOf,
- IrInstSrcIdTestNonNull,
- IrInstSrcIdOptionalUnwrapPtr,
- IrInstSrcIdClz,
- IrInstSrcIdCtz,
- IrInstSrcIdPopCount,
- IrInstSrcIdBswap,
- IrInstSrcIdBitReverse,
- IrInstSrcIdImport,
- IrInstSrcIdCImport,
- IrInstSrcIdCInclude,
- IrInstSrcIdCDefine,
- IrInstSrcIdCUndef,
- IrInstSrcIdRef,
- IrInstSrcIdCompileErr,
- IrInstSrcIdCompileLog,
- IrInstSrcIdErrName,
- IrInstSrcIdEmbedFile,
- IrInstSrcIdCmpxchg,
- IrInstSrcIdFence,
- IrInstSrcIdTruncate,
- IrInstSrcIdIntCast,
- IrInstSrcIdFloatCast,
- IrInstSrcIdIntToFloat,
- IrInstSrcIdFloatToInt,
- IrInstSrcIdBoolToInt,
- IrInstSrcIdVectorType,
- IrInstSrcIdShuffleVector,
- IrInstSrcIdSplat,
- IrInstSrcIdBoolNot,
- IrInstSrcIdMemset,
- IrInstSrcIdMemcpy,
- IrInstSrcIdSlice,
- IrInstSrcIdBreakpoint,
- IrInstSrcIdReturnAddress,
- IrInstSrcIdFrameAddress,
- IrInstSrcIdFrameHandle,
- IrInstSrcIdFrameType,
- IrInstSrcIdFrameSize,
- IrInstSrcIdAlignOf,
- IrInstSrcIdOverflowOp,
- IrInstSrcIdTestErr,
- IrInstSrcIdMulAdd,
- IrInstSrcIdFloatOp,
- IrInstSrcIdUnwrapErrCode,
- IrInstSrcIdUnwrapErrPayload,
- IrInstSrcIdFnProto,
- IrInstSrcIdTestComptime,
- IrInstSrcIdPtrCast,
- IrInstSrcIdBitCast,
- IrInstSrcIdIntToPtr,
- IrInstSrcIdPtrToInt,
- IrInstSrcIdIntToEnum,
- IrInstSrcIdEnumToInt,
- IrInstSrcIdIntToErr,
- IrInstSrcIdErrToInt,
- IrInstSrcIdCheckSwitchProngs,
- IrInstSrcIdCheckStatementIsVoid,
- IrInstSrcIdTypeName,
- IrInstSrcIdDeclRef,
- IrInstSrcIdPanic,
- IrInstSrcIdTagName,
- IrInstSrcIdTagType,
- IrInstSrcIdFieldParentPtr,
- IrInstSrcIdByteOffsetOf,
- IrInstSrcIdBitOffsetOf,
- IrInstSrcIdTypeInfo,
- IrInstSrcIdType,
- IrInstSrcIdHasField,
- IrInstSrcIdSetEvalBranchQuota,
- IrInstSrcIdPtrType,
- IrInstSrcIdAlignCast,
- IrInstSrcIdImplicitCast,
- IrInstSrcIdResolveResult,
- IrInstSrcIdResetResult,
- IrInstSrcIdSetAlignStack,
- IrInstSrcIdArgType,
- IrInstSrcIdExport,
- IrInstSrcIdErrorReturnTrace,
- IrInstSrcIdErrorUnion,
- IrInstSrcIdAtomicRmw,
- IrInstSrcIdAtomicLoad,
- IrInstSrcIdAtomicStore,
- IrInstSrcIdSaveErrRetAddr,
- IrInstSrcIdAddImplicitReturnType,
- IrInstSrcIdErrSetCast,
- IrInstSrcIdCheckRuntimeScope,
- IrInstSrcIdHasDecl,
- IrInstSrcIdUndeclaredIdent,
- IrInstSrcIdAlloca,
- IrInstSrcIdEndExpr,
- IrInstSrcIdUnionInitNamedField,
- IrInstSrcIdSuspendBegin,
- IrInstSrcIdSuspendFinish,
- IrInstSrcIdAwait,
- IrInstSrcIdResume,
- IrInstSrcIdSpillBegin,
- IrInstSrcIdSpillEnd,
- IrInstSrcIdWasmMemorySize,
- IrInstSrcIdWasmMemoryGrow,
- IrInstSrcIdSrc,
-};
-
-// ir_render_* functions in codegen.cpp consume Gen instructions and produce LLVM IR.
-// Src instructions do not have type information; Gen instructions do.
-enum IrInstGenId {
- IrInstGenIdInvalid,
- IrInstGenIdDeclVar,
- IrInstGenIdBr,
- IrInstGenIdCondBr,
- IrInstGenIdSwitchBr,
- IrInstGenIdPhi,
- IrInstGenIdBinaryNot,
- IrInstGenIdNegation,
- IrInstGenIdNegationWrapping,
- IrInstGenIdBinOp,
- IrInstGenIdLoadPtr,
- IrInstGenIdStorePtr,
- IrInstGenIdVectorStoreElem,
- IrInstGenIdStructFieldPtr,
- IrInstGenIdUnionFieldPtr,
- IrInstGenIdElemPtr,
- IrInstGenIdVarPtr,
- IrInstGenIdReturnPtr,
- IrInstGenIdCall,
- IrInstGenIdReturn,
- IrInstGenIdCast,
- IrInstGenIdUnreachable,
- IrInstGenIdAsm,
- IrInstGenIdTestNonNull,
- IrInstGenIdOptionalUnwrapPtr,
- IrInstGenIdOptionalWrap,
- IrInstGenIdUnionTag,
- IrInstGenIdClz,
- IrInstGenIdCtz,
- IrInstGenIdPopCount,
- IrInstGenIdBswap,
- IrInstGenIdBitReverse,
- IrInstGenIdRef,
- IrInstGenIdErrName,
- IrInstGenIdCmpxchg,
- IrInstGenIdFence,
- IrInstGenIdTruncate,
- IrInstGenIdShuffleVector,
- IrInstGenIdSplat,
- IrInstGenIdBoolNot,
- IrInstGenIdMemset,
- IrInstGenIdMemcpy,
- IrInstGenIdSlice,
- IrInstGenIdBreakpoint,
- IrInstGenIdReturnAddress,
- IrInstGenIdFrameAddress,
- IrInstGenIdFrameHandle,
- IrInstGenIdFrameSize,
- IrInstGenIdOverflowOp,
- IrInstGenIdTestErr,
- IrInstGenIdMulAdd,
- IrInstGenIdFloatOp,
- IrInstGenIdUnwrapErrCode,
- IrInstGenIdUnwrapErrPayload,
- IrInstGenIdErrWrapCode,
- IrInstGenIdErrWrapPayload,
- IrInstGenIdPtrCast,
- IrInstGenIdBitCast,
- IrInstGenIdWidenOrShorten,
- IrInstGenIdIntToPtr,
- IrInstGenIdPtrToInt,
- IrInstGenIdIntToEnum,
- IrInstGenIdIntToErr,
- IrInstGenIdErrToInt,
- IrInstGenIdPanic,
- IrInstGenIdTagName,
- IrInstGenIdFieldParentPtr,
- IrInstGenIdAlignCast,
- IrInstGenIdErrorReturnTrace,
- IrInstGenIdAtomicRmw,
- IrInstGenIdAtomicLoad,
- IrInstGenIdAtomicStore,
- IrInstGenIdSaveErrRetAddr,
- IrInstGenIdVectorToArray,
- IrInstGenIdArrayToVector,
- IrInstGenIdAssertZero,
- IrInstGenIdAssertNonNull,
- IrInstGenIdPtrOfArrayToSlice,
- IrInstGenIdSuspendBegin,
- IrInstGenIdSuspendFinish,
- IrInstGenIdAwait,
- IrInstGenIdResume,
- IrInstGenIdSpillBegin,
- IrInstGenIdSpillEnd,
- IrInstGenIdVectorExtractElem,
- IrInstGenIdAlloca,
- IrInstGenIdConst,
- IrInstGenIdWasmMemorySize,
- IrInstGenIdWasmMemoryGrow,
-};
-
-// Common fields between IrInstSrc and IrInstGen. This allows future passes
-// after pass2 to be added to zig.
-struct IrInst {
- // if ref_count is zero and the instruction has no side effects,
- // the instruction can be omitted in codegen
- uint32_t ref_count;
- uint32_t debug_id;
-
- Scope *scope;
- AstNode *source_node;
-
- // for debugging purposes, these are useful to call to inspect the instruction
- void dump();
- void src();
-};
-
-struct IrInstSrc {
- IrInst base;
-
- IrInstSrcId id;
- // true if this instruction was generated by zig and not from user code
- // this matters for the "unreachable code" compile error
- bool is_gen;
- bool is_noreturn;
-
- // When analyzing IR, instructions that point to this instruction in the "old ir"
- // can find the instruction that corresponds to this value in the "new ir"
- // with this child field.
- IrInstGen *child;
- IrBasicBlockSrc *owner_bb;
-
- // for debugging purposes, these are useful to call to inspect the instruction
- void dump();
- void src();
-};
-
-struct IrInstGen {
- IrInst base;
-
- IrInstGenId id;
-
- LLVMValueRef llvm_value;
- ZigValue *value;
- IrBasicBlockGen *owner_bb;
- // Nearly any instruction can have to be stored as a local variable before suspending
- // and then loaded after resuming, in case there is an expression with a suspend point
- // in it, such as: x + await y
- IrInstGen *spill;
-
- // for debugging purposes, these are useful to call to inspect the instruction
- void dump();
- void src();
-};
-
-struct IrInstSrcDeclVar {
- IrInstSrc base;
-
- ZigVar *var;
- IrInstSrc *var_type;
- IrInstSrc *align_value;
- IrInstSrc *ptr;
-};
-
-struct IrInstGenDeclVar {
- IrInstGen base;
-
- ZigVar *var;
- IrInstGen *var_ptr;
-};
-
-struct IrInstSrcCondBr {
- IrInstSrc base;
-
- IrInstSrc *condition;
- IrBasicBlockSrc *then_block;
- IrBasicBlockSrc *else_block;
- IrInstSrc *is_comptime;
- ResultLoc *result_loc;
-};
-
-struct IrInstGenCondBr {
- IrInstGen base;
-
- IrInstGen *condition;
- IrBasicBlockGen *then_block;
- IrBasicBlockGen *else_block;
-};
-
-struct IrInstSrcBr {
- IrInstSrc base;
-
- IrBasicBlockSrc *dest_block;
- IrInstSrc *is_comptime;
-};
-
-struct IrInstGenBr {
- IrInstGen base;
-
- IrBasicBlockGen *dest_block;
-};
-
-struct IrInstSrcSwitchBrCase {
- IrInstSrc *value;
- IrBasicBlockSrc *block;
-};
-
-struct IrInstSrcSwitchBr {
- IrInstSrc base;
-
- IrInstSrc *target_value;
- IrBasicBlockSrc *else_block;
- size_t case_count;
- IrInstSrcSwitchBrCase *cases;
- IrInstSrc *is_comptime;
- IrInstSrc *switch_prongs_void;
-};
-
-struct IrInstGenSwitchBrCase {
- IrInstGen *value;
- IrBasicBlockGen *block;
-};
-
-struct IrInstGenSwitchBr {
- IrInstGen base;
-
- IrInstGen *target_value;
- IrBasicBlockGen *else_block;
- size_t case_count;
- IrInstGenSwitchBrCase *cases;
-};
-
-struct IrInstSrcSwitchVar {
- IrInstSrc base;
-
- IrInstSrc *target_value_ptr;
- IrInstSrc **prongs_ptr;
- size_t prongs_len;
-};
-
-struct IrInstSrcSwitchElseVar {
- IrInstSrc base;
-
- IrInstSrc *target_value_ptr;
- IrInstSrcSwitchBr *switch_br;
-};
-
-struct IrInstSrcSwitchTarget {
- IrInstSrc base;
-
- IrInstSrc *target_value_ptr;
-};
-
-struct IrInstSrcPhi {
- IrInstSrc base;
-
- size_t incoming_count;
- IrBasicBlockSrc **incoming_blocks;
- IrInstSrc **incoming_values;
- ResultLocPeerParent *peer_parent;
-};
-
-struct IrInstGenPhi {
- IrInstGen base;
-
- size_t incoming_count;
- IrBasicBlockGen **incoming_blocks;
- IrInstGen **incoming_values;
-};
-
-enum IrUnOp {
- IrUnOpInvalid,
- IrUnOpBinNot,
- IrUnOpNegation,
- IrUnOpNegationWrap,
- IrUnOpDereference,
- IrUnOpOptional,
-};
-
-struct IrInstSrcUnOp {
- IrInstSrc base;
-
- IrUnOp op_id;
- LVal lval;
- IrInstSrc *value;
- ResultLoc *result_loc;
-};
-
-struct IrInstGenBinaryNot {
- IrInstGen base;
- IrInstGen *operand;
-};
-
-struct IrInstGenNegation {
- IrInstGen base;
- IrInstGen *operand;
-};
-
-struct IrInstGenNegationWrapping {
- IrInstGen base;
- IrInstGen *operand;
-};
-
-enum IrBinOp {
- IrBinOpInvalid,
- IrBinOpBoolOr,
- IrBinOpBoolAnd,
- IrBinOpCmpEq,
- IrBinOpCmpNotEq,
- IrBinOpCmpLessThan,
- IrBinOpCmpGreaterThan,
- IrBinOpCmpLessOrEq,
- IrBinOpCmpGreaterOrEq,
- IrBinOpBinOr,
- IrBinOpBinXor,
- IrBinOpBinAnd,
- IrBinOpBitShiftLeftLossy,
- IrBinOpBitShiftLeftExact,
- IrBinOpBitShiftRightLossy,
- IrBinOpBitShiftRightExact,
- IrBinOpAdd,
- IrBinOpAddWrap,
- IrBinOpSub,
- IrBinOpSubWrap,
- IrBinOpMult,
- IrBinOpMultWrap,
- IrBinOpDivUnspecified,
- IrBinOpDivExact,
- IrBinOpDivTrunc,
- IrBinOpDivFloor,
- IrBinOpRemUnspecified,
- IrBinOpRemRem,
- IrBinOpRemMod,
- IrBinOpArrayCat,
- IrBinOpArrayMult,
-};
-
-struct IrInstSrcBinOp {
- IrInstSrc base;
-
- IrInstSrc *op1;
- IrInstSrc *op2;
- IrBinOp op_id;
- bool safety_check_on;
-};
-
-struct IrInstGenBinOp {
- IrInstGen base;
-
- IrInstGen *op1;
- IrInstGen *op2;
- IrBinOp op_id;
- bool safety_check_on;
-};
-
-struct IrInstSrcMergeErrSets {
- IrInstSrc base;
-
- IrInstSrc *op1;
- IrInstSrc *op2;
- Buf *type_name;
-};
-
-struct IrInstSrcLoadPtr {
- IrInstSrc base;
-
- IrInstSrc *ptr;
-};
-
-struct IrInstGenLoadPtr {
- IrInstGen base;
-
- IrInstGen *ptr;
- IrInstGen *result_loc;
-};
-
-struct IrInstSrcStorePtr {
- IrInstSrc base;
-
- IrInstSrc *ptr;
- IrInstSrc *value;
-
- bool allow_write_through_const;
-};
-
-struct IrInstGenStorePtr {
- IrInstGen base;
-
- IrInstGen *ptr;
- IrInstGen *value;
-};
-
-struct IrInstGenVectorStoreElem {
- IrInstGen base;
-
- IrInstGen *vector_ptr;
- IrInstGen *index;
- IrInstGen *value;
-};
-
-struct IrInstSrcFieldPtr {
- IrInstSrc base;
-
- IrInstSrc *container_ptr;
- Buf *field_name_buffer;
- IrInstSrc *field_name_expr;
- bool initializing;
-};
-
-struct IrInstGenStructFieldPtr {
- IrInstGen base;
-
- IrInstGen *struct_ptr;
- TypeStructField *field;
- bool is_const;
-};
-
-struct IrInstGenUnionFieldPtr {
- IrInstGen base;
-
- IrInstGen *union_ptr;
- TypeUnionField *field;
- bool safety_check_on;
- bool initializing;
-};
-
-struct IrInstSrcElemPtr {
- IrInstSrc base;
-
- IrInstSrc *array_ptr;
- IrInstSrc *elem_index;
- AstNode *init_array_type_source_node;
- PtrLen ptr_len;
- bool safety_check_on;
-};
-
-struct IrInstGenElemPtr {
- IrInstGen base;
-
- IrInstGen *array_ptr;
- IrInstGen *elem_index;
- bool safety_check_on;
-};
-
-struct IrInstSrcVarPtr {
- IrInstSrc base;
-
- ZigVar *var;
- ScopeFnDef *crossed_fndef_scope;
-};
-
-struct IrInstGenVarPtr {
- IrInstGen base;
-
- ZigVar *var;
-};
-
-// For functions that have a return type for which handle_is_ptr is true, a
-// result location pointer is the secret first parameter ("sret"). This
-// instruction returns that pointer.
-struct IrInstGenReturnPtr {
- IrInstGen base;
-};
-
-struct IrInstSrcCall {
- IrInstSrc base;
-
- IrInstSrc *fn_ref;
- ZigFn *fn_entry;
- size_t arg_count;
- IrInstSrc **args;
- IrInstSrc *ret_ptr;
- ResultLoc *result_loc;
-
- IrInstSrc *new_stack;
-
- CallModifier modifier;
- bool is_async_call_builtin;
-};
-
-// This is a pass1 instruction, used by @call when the args node is
-// a tuple or struct literal.
-struct IrInstSrcCallArgs {
- IrInstSrc base;
-
- IrInstSrc *options;
- IrInstSrc *fn_ref;
- IrInstSrc **args_ptr;
- size_t args_len;
- ResultLoc *result_loc;
-};
-
-// This is a pass1 instruction, used by @call, when the args node
-// is not a literal.
-// `args` is expected to be either a struct or a tuple.
-struct IrInstSrcCallExtra {
- IrInstSrc base;
-
- IrInstSrc *options;
- IrInstSrc *fn_ref;
- IrInstSrc *args;
- ResultLoc *result_loc;
-};
-
-// This is a pass1 instruction, used by @asyncCall, when the args node
-// is not a literal.
-// `args` is expected to be either a struct or a tuple.
-struct IrInstSrcAsyncCallExtra {
- IrInstSrc base;
-
- CallModifier modifier;
- IrInstSrc *fn_ref;
- IrInstSrc *ret_ptr;
- IrInstSrc *new_stack;
- IrInstSrc *args;
- ResultLoc *result_loc;
-};
-
-struct IrInstGenCall {
- IrInstGen base;
-
- IrInstGen *fn_ref;
- ZigFn *fn_entry;
- size_t arg_count;
- IrInstGen **args;
- IrInstGen *result_loc;
- IrInstGen *frame_result_loc;
- IrInstGen *new_stack;
-
- CallModifier modifier;
-
- bool is_async_call_builtin;
-};
-
-struct IrInstSrcConst {
- IrInstSrc base;
-
- ZigValue *value;
-};
-
-struct IrInstGenConst {
- IrInstGen base;
-};
-
-struct IrInstSrcReturn {
- IrInstSrc base;
-
- IrInstSrc *operand;
-};
-
-// When an IrExecutable is not in a function, a return instruction means that
-// the expression returns with that value, even though a return statement from
-// an AST perspective is invalid.
-struct IrInstGenReturn {
- IrInstGen base;
-
- IrInstGen *operand;
-};
-
-enum CastOp {
- CastOpNoCast, // signifies the function call expression is not a cast
- CastOpNoop, // fn call expr is a cast, but does nothing
- CastOpIntToFloat,
- CastOpFloatToInt,
- CastOpBoolToInt,
- CastOpNumLitToConcrete,
- CastOpErrSet,
- CastOpBitCast,
-};
-
-// TODO get rid of this instruction, replace with instructions for each op code
-struct IrInstGenCast {
- IrInstGen base;
-
- IrInstGen *value;
- CastOp cast_op;
-};
-
-struct IrInstSrcContainerInitList {
- IrInstSrc base;
-
- IrInstSrc *elem_type;
- size_t item_count;
- IrInstSrc **elem_result_loc_list;
- IrInstSrc *result_loc;
- AstNode *init_array_type_source_node;
-};
-
-struct IrInstSrcContainerInitFieldsField {
- Buf *name;
- AstNode *source_node;
- IrInstSrc *result_loc;
-};
-
-struct IrInstSrcContainerInitFields {
- IrInstSrc base;
-
- size_t field_count;
- IrInstSrcContainerInitFieldsField *fields;
- IrInstSrc *result_loc;
-};
-
-struct IrInstSrcUnreachable {
- IrInstSrc base;
-};
-
-struct IrInstGenUnreachable {
- IrInstGen base;
-};
-
-struct IrInstSrcTypeOf {
- IrInstSrc base;
-
- union {
- IrInstSrc *scalar; // value_count == 1
- IrInstSrc **list; // value_count > 1
- } value;
- size_t value_count;
-};
-
-struct IrInstSrcSetCold {
- IrInstSrc base;
-
- IrInstSrc *is_cold;
-};
-
-struct IrInstSrcSetRuntimeSafety {
- IrInstSrc base;
-
- IrInstSrc *safety_on;
-};
-
-struct IrInstSrcSetFloatMode {
- IrInstSrc base;
-
- IrInstSrc *scope_value;
- IrInstSrc *mode_value;
-};
-
-struct IrInstSrcArrayType {
- IrInstSrc base;
-
- IrInstSrc *size;
- IrInstSrc *sentinel;
- IrInstSrc *child_type;
-};
-
-struct IrInstSrcPtrType {
- IrInstSrc base;
-
- IrInstSrc *sentinel;
- IrInstSrc *align_value;
- IrInstSrc *child_type;
- uint32_t bit_offset_start;
- uint32_t host_int_bytes;
- PtrLen ptr_len;
- bool is_const;
- bool is_volatile;
- bool is_allow_zero;
-};
-
-struct IrInstSrcAnyFrameType {
- IrInstSrc base;
-
- IrInstSrc *payload_type;
-};
-
-struct IrInstSrcSliceType {
- IrInstSrc base;
-
- IrInstSrc *sentinel;
- IrInstSrc *align_value;
- IrInstSrc *child_type;
- bool is_const;
- bool is_volatile;
- bool is_allow_zero;
-};
-
-struct IrInstSrcAsm {
- IrInstSrc base;
-
- IrInstSrc *asm_template;
- IrInstSrc **input_list;
- IrInstSrc **output_types;
- ZigVar **output_vars;
- size_t return_count;
- bool has_side_effects;
- bool is_global;
-};
-
-struct IrInstGenAsm {
- IrInstGen base;
-
- Buf *asm_template;
- AsmToken *token_list;
- size_t token_list_len;
- IrInstGen **input_list;
- IrInstGen **output_types;
- ZigVar **output_vars;
- size_t return_count;
- bool has_side_effects;
-};
-
-struct IrInstSrcSizeOf {
- IrInstSrc base;
-
- IrInstSrc *type_value;
- bool bit_size;
-};
-
-// returns true if nonnull, returns false if null
-struct IrInstSrcTestNonNull {
- IrInstSrc base;
-
- IrInstSrc *value;
-};
-
-struct IrInstGenTestNonNull {
- IrInstGen base;
-
- IrInstGen *value;
-};
-
-// Takes a pointer to an optional value, returns a pointer
-// to the payload.
-struct IrInstSrcOptionalUnwrapPtr {
- IrInstSrc base;
-
- IrInstSrc *base_ptr;
- bool safety_check_on;
-};
-
-struct IrInstGenOptionalUnwrapPtr {
- IrInstGen base;
-
- IrInstGen *base_ptr;
- bool safety_check_on;
- bool initializing;
-};
-
-struct IrInstSrcCtz {
- IrInstSrc base;
-
- IrInstSrc *type;
- IrInstSrc *op;
-};
-
-struct IrInstGenCtz {
- IrInstGen base;
-
- IrInstGen *op;
-};
-
-struct IrInstSrcClz {
- IrInstSrc base;
-
- IrInstSrc *type;
- IrInstSrc *op;
-};
-
-struct IrInstGenClz {
- IrInstGen base;
-
- IrInstGen *op;
-};
-
-struct IrInstSrcPopCount {
- IrInstSrc base;
-
- IrInstSrc *type;
- IrInstSrc *op;
-};
-
-struct IrInstGenPopCount {
- IrInstGen base;
-
- IrInstGen *op;
-};
-
-struct IrInstGenUnionTag {
- IrInstGen base;
-
- IrInstGen *value;
-};
-
-struct IrInstSrcImport {
- IrInstSrc base;
-
- IrInstSrc *name;
-};
-
-struct IrInstSrcRef {
- IrInstSrc base;
-
- IrInstSrc *value;
-};
-
-struct IrInstGenRef {
- IrInstGen base;
-
- IrInstGen *operand;
- IrInstGen *result_loc;
-};
-
-struct IrInstSrcCompileErr {
- IrInstSrc base;
-
- IrInstSrc *msg;
-};
-
-struct IrInstSrcCompileLog {
- IrInstSrc base;
-
- size_t msg_count;
- IrInstSrc **msg_list;
-};
-
-struct IrInstSrcErrName {
- IrInstSrc base;
-
- IrInstSrc *value;
-};
-
-struct IrInstGenErrName {
- IrInstGen base;
-
- IrInstGen *value;
-};
-
-struct IrInstSrcCImport {
- IrInstSrc base;
-};
-
-struct IrInstSrcCInclude {
- IrInstSrc base;
-
- IrInstSrc *name;
-};
-
-struct IrInstSrcCDefine {
- IrInstSrc base;
-
- IrInstSrc *name;
- IrInstSrc *value;
-};
-
-struct IrInstSrcCUndef {
- IrInstSrc base;
-
- IrInstSrc *name;
-};
-
-struct IrInstSrcEmbedFile {
- IrInstSrc base;
-
- IrInstSrc *name;
-};
-
-struct IrInstSrcCmpxchg {
- IrInstSrc base;
-
- bool is_weak;
- IrInstSrc *type_value;
- IrInstSrc *ptr;
- IrInstSrc *cmp_value;
- IrInstSrc *new_value;
- IrInstSrc *success_order_value;
- IrInstSrc *failure_order_value;
- ResultLoc *result_loc;
-};
-
-struct IrInstGenCmpxchg {
- IrInstGen base;
-
- AtomicOrder success_order;
- AtomicOrder failure_order;
- IrInstGen *ptr;
- IrInstGen *cmp_value;
- IrInstGen *new_value;
- IrInstGen *result_loc;
- bool is_weak;
-};
-
-struct IrInstSrcFence {
- IrInstSrc base;
-
- IrInstSrc *order;
-};
-
-struct IrInstGenFence {
- IrInstGen base;
-
- AtomicOrder order;
-};
-
-struct IrInstSrcTruncate {
- IrInstSrc base;
-
- IrInstSrc *dest_type;
- IrInstSrc *target;
-};
-
-struct IrInstGenTruncate {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstSrcIntCast {
- IrInstSrc base;
-
- IrInstSrc *dest_type;
- IrInstSrc *target;
-};
-
-struct IrInstSrcFloatCast {
- IrInstSrc base;
-
- IrInstSrc *dest_type;
- IrInstSrc *target;
-};
-
-struct IrInstSrcErrSetCast {
- IrInstSrc base;
-
- IrInstSrc *dest_type;
- IrInstSrc *target;
-};
-
-struct IrInstSrcIntToFloat {
- IrInstSrc base;
-
- IrInstSrc *dest_type;
- IrInstSrc *target;
-};
-
-struct IrInstSrcFloatToInt {
- IrInstSrc base;
-
- IrInstSrc *dest_type;
- IrInstSrc *target;
-};
-
-struct IrInstSrcBoolToInt {
- IrInstSrc base;
-
- IrInstSrc *target;
-};
-
-struct IrInstSrcVectorType {
- IrInstSrc base;
-
- IrInstSrc *len;
- IrInstSrc *elem_type;
-};
-
-struct IrInstSrcBoolNot {
- IrInstSrc base;
-
- IrInstSrc *value;
-};
-
-struct IrInstGenBoolNot {
- IrInstGen base;
-
- IrInstGen *value;
-};
-
-struct IrInstSrcMemset {
- IrInstSrc base;
-
- IrInstSrc *dest_ptr;
- IrInstSrc *byte;
- IrInstSrc *count;
-};
-
-struct IrInstGenMemset {
- IrInstGen base;
-
- IrInstGen *dest_ptr;
- IrInstGen *byte;
- IrInstGen *count;
-};
-
-struct IrInstSrcMemcpy {
- IrInstSrc base;
-
- IrInstSrc *dest_ptr;
- IrInstSrc *src_ptr;
- IrInstSrc *count;
-};
-
-struct IrInstGenMemcpy {
- IrInstGen base;
-
- IrInstGen *dest_ptr;
- IrInstGen *src_ptr;
- IrInstGen *count;
-};
-
-struct IrInstSrcWasmMemorySize {
- IrInstSrc base;
-
- IrInstSrc *index;
-};
-
-struct IrInstGenWasmMemorySize {
- IrInstGen base;
-
- IrInstGen *index;
-};
-
-struct IrInstSrcWasmMemoryGrow {
- IrInstSrc base;
-
- IrInstSrc *index;
- IrInstSrc *delta;
-};
-
-struct IrInstGenWasmMemoryGrow {
- IrInstGen base;
-
- IrInstGen *index;
- IrInstGen *delta;
-};
-
-struct IrInstSrcSrc {
- IrInstSrc base;
-};
-
-struct IrInstSrcSlice {
- IrInstSrc base;
-
- IrInstSrc *ptr;
- IrInstSrc *start;
- IrInstSrc *end;
- IrInstSrc *sentinel;
- ResultLoc *result_loc;
- bool safety_check_on;
-};
-
-struct IrInstGenSlice {
- IrInstGen base;
-
- IrInstGen *ptr;
- IrInstGen *start;
- IrInstGen *end;
- IrInstGen *result_loc;
- ZigValue *sentinel;
- bool safety_check_on;
-};
-
-struct IrInstSrcBreakpoint {
- IrInstSrc base;
-};
-
-struct IrInstGenBreakpoint {
- IrInstGen base;
-};
-
-struct IrInstSrcReturnAddress {
- IrInstSrc base;
-};
-
-struct IrInstGenReturnAddress {
- IrInstGen base;
-};
-
-struct IrInstSrcFrameAddress {
- IrInstSrc base;
-};
-
-struct IrInstGenFrameAddress {
- IrInstGen base;
-};
-
-struct IrInstSrcFrameHandle {
- IrInstSrc base;
-};
-
-struct IrInstGenFrameHandle {
- IrInstGen base;
-};
-
-struct IrInstSrcFrameType {
- IrInstSrc base;
-
- IrInstSrc *fn;
-};
-
-struct IrInstSrcFrameSize {
- IrInstSrc base;
-
- IrInstSrc *fn;
-};
-
-struct IrInstGenFrameSize {
- IrInstGen base;
-
- IrInstGen *fn;
-};
-
-enum IrOverflowOp {
- IrOverflowOpAdd,
- IrOverflowOpSub,
- IrOverflowOpMul,
- IrOverflowOpShl,
-};
-
-struct IrInstSrcOverflowOp {
- IrInstSrc base;
-
- IrOverflowOp op;
- IrInstSrc *type_value;
- IrInstSrc *op1;
- IrInstSrc *op2;
- IrInstSrc *result_ptr;
-};
-
-struct IrInstGenOverflowOp {
- IrInstGen base;
-
- IrOverflowOp op;
- IrInstGen *op1;
- IrInstGen *op2;
- IrInstGen *result_ptr;
-
- // TODO can this field be removed?
- ZigType *result_ptr_type;
-};
-
-struct IrInstSrcMulAdd {
- IrInstSrc base;
-
- IrInstSrc *type_value;
- IrInstSrc *op1;
- IrInstSrc *op2;
- IrInstSrc *op3;
-};
-
-struct IrInstGenMulAdd {
- IrInstGen base;
-
- IrInstGen *op1;
- IrInstGen *op2;
- IrInstGen *op3;
-};
-
-struct IrInstSrcAlignOf {
- IrInstSrc base;
-
- IrInstSrc *type_value;
-};
-
-// returns true if error, returns false if not error
-struct IrInstSrcTestErr {
- IrInstSrc base;
-
- IrInstSrc *base_ptr;
- bool resolve_err_set;
- bool base_ptr_is_payload;
-};
-
-struct IrInstGenTestErr {
- IrInstGen base;
-
- IrInstGen *err_union;
-};
-
-// Takes an error union pointer, returns a pointer to the error code.
-struct IrInstSrcUnwrapErrCode {
- IrInstSrc base;
-
- IrInstSrc *err_union_ptr;
- bool initializing;
-};
-
-struct IrInstGenUnwrapErrCode {
- IrInstGen base;
-
- IrInstGen *err_union_ptr;
- bool initializing;
-};
-
-struct IrInstSrcUnwrapErrPayload {
- IrInstSrc base;
-
- IrInstSrc *value;
- bool safety_check_on;
- bool initializing;
-};
-
-struct IrInstGenUnwrapErrPayload {
- IrInstGen base;
-
- IrInstGen *value;
- bool safety_check_on;
- bool initializing;
-};
-
-struct IrInstGenOptionalWrap {
- IrInstGen base;
-
- IrInstGen *operand;
- IrInstGen *result_loc;
-};
-
-struct IrInstGenErrWrapPayload {
- IrInstGen base;
-
- IrInstGen *operand;
- IrInstGen *result_loc;
-};
-
-struct IrInstGenErrWrapCode {
- IrInstGen base;
-
- IrInstGen *operand;
- IrInstGen *result_loc;
-};
-
-struct IrInstSrcFnProto {
- IrInstSrc base;
-
- IrInstSrc **param_types;
- IrInstSrc *align_value;
- IrInstSrc *callconv_value;
- IrInstSrc *return_type;
- bool is_var_args;
-};
-
-// true if the target value is compile time known, false otherwise
-struct IrInstSrcTestComptime {
- IrInstSrc base;
-
- IrInstSrc *value;
-};
-
-struct IrInstSrcPtrCast {
- IrInstSrc base;
-
- IrInstSrc *dest_type;
- IrInstSrc *ptr;
- bool safety_check_on;
-};
-
-struct IrInstGenPtrCast {
- IrInstGen base;
-
- IrInstGen *ptr;
- bool safety_check_on;
-};
-
-struct IrInstSrcImplicitCast {
- IrInstSrc base;
-
- IrInstSrc *operand;
- ResultLocCast *result_loc_cast;
-};
-
-struct IrInstSrcBitCast {
- IrInstSrc base;
-
- IrInstSrc *operand;
- ResultLocBitCast *result_loc_bit_cast;
-};
-
-struct IrInstGenBitCast {
- IrInstGen base;
-
- IrInstGen *operand;
-};
-
-struct IrInstGenWidenOrShorten {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstSrcPtrToInt {
- IrInstSrc base;
-
- IrInstSrc *target;
-};
-
-struct IrInstGenPtrToInt {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstSrcIntToPtr {
- IrInstSrc base;
-
- IrInstSrc *dest_type;
- IrInstSrc *target;
-};
-
-struct IrInstGenIntToPtr {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstSrcIntToEnum {
- IrInstSrc base;
-
- IrInstSrc *dest_type;
- IrInstSrc *target;
-};
-
-struct IrInstGenIntToEnum {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstSrcEnumToInt {
- IrInstSrc base;
-
- IrInstSrc *target;
-};
-
-struct IrInstSrcIntToErr {
- IrInstSrc base;
-
- IrInstSrc *target;
-};
-
-struct IrInstGenIntToErr {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstSrcErrToInt {
- IrInstSrc base;
-
- IrInstSrc *target;
-};
-
-struct IrInstGenErrToInt {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstSrcCheckSwitchProngsRange {
- IrInstSrc *start;
- IrInstSrc *end;
-};
-
-struct IrInstSrcCheckSwitchProngs {
- IrInstSrc base;
-
- IrInstSrc *target_value;
- IrInstSrcCheckSwitchProngsRange *ranges;
- size_t range_count;
- AstNode* else_prong;
- bool have_underscore_prong;
-};
-
-struct IrInstSrcCheckStatementIsVoid {
- IrInstSrc base;
-
- IrInstSrc *statement_value;
-};
-
-struct IrInstSrcTypeName {
- IrInstSrc base;
-
- IrInstSrc *type_value;
-};
-
-struct IrInstSrcDeclRef {
- IrInstSrc base;
-
- LVal lval;
- Tld *tld;
-};
-
-struct IrInstSrcPanic {
- IrInstSrc base;
-
- IrInstSrc *msg;
-};
-
-struct IrInstGenPanic {
- IrInstGen base;
-
- IrInstGen *msg;
-};
-
-struct IrInstSrcTagName {
- IrInstSrc base;
-
- IrInstSrc *target;
-};
-
-struct IrInstGenTagName {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstSrcTagType {
- IrInstSrc base;
-
- IrInstSrc *target;
-};
-
-struct IrInstSrcFieldParentPtr {
- IrInstSrc base;
-
- IrInstSrc *type_value;
- IrInstSrc *field_name;
- IrInstSrc *field_ptr;
-};
-
-struct IrInstGenFieldParentPtr {
- IrInstGen base;
-
- IrInstGen *field_ptr;
- TypeStructField *field;
-};
-
-struct IrInstSrcByteOffsetOf {
- IrInstSrc base;
-
- IrInstSrc *type_value;
- IrInstSrc *field_name;
-};
-
-struct IrInstSrcBitOffsetOf {
- IrInstSrc base;
-
- IrInstSrc *type_value;
- IrInstSrc *field_name;
-};
-
-struct IrInstSrcTypeInfo {
- IrInstSrc base;
-
- IrInstSrc *type_value;
-};
-
-struct IrInstSrcType {
- IrInstSrc base;
-
- IrInstSrc *type_info;
-};
-
-struct IrInstSrcHasField {
- IrInstSrc base;
-
- IrInstSrc *container_type;
- IrInstSrc *field_name;
-};
-
-struct IrInstSrcSetEvalBranchQuota {
- IrInstSrc base;
-
- IrInstSrc *new_quota;
-};
-
-struct IrInstSrcAlignCast {
- IrInstSrc base;
-
- IrInstSrc *align_bytes;
- IrInstSrc *target;
-};
-
-struct IrInstGenAlignCast {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstSrcSetAlignStack {
- IrInstSrc base;
-
- IrInstSrc *align_bytes;
-};
-
-struct IrInstSrcArgType {
- IrInstSrc base;
-
- IrInstSrc *fn_type;
- IrInstSrc *arg_index;
- bool allow_var;
-};
-
-struct IrInstSrcExport {
- IrInstSrc base;
-
- IrInstSrc *target;
- IrInstSrc *options;
-};
-
-enum IrInstErrorReturnTraceOptional {
- IrInstErrorReturnTraceNull,
- IrInstErrorReturnTraceNonNull,
-};
-
-struct IrInstSrcErrorReturnTrace {
- IrInstSrc base;
-
- IrInstErrorReturnTraceOptional optional;
-};
-
-struct IrInstGenErrorReturnTrace {
- IrInstGen base;
-
- IrInstErrorReturnTraceOptional optional;
-};
-
-struct IrInstSrcErrorUnion {
- IrInstSrc base;
-
- IrInstSrc *err_set;
- IrInstSrc *payload;
- Buf *type_name;
-};
-
-struct IrInstSrcAtomicRmw {
- IrInstSrc base;
-
- IrInstSrc *operand_type;
- IrInstSrc *ptr;
- IrInstSrc *op;
- IrInstSrc *operand;
- IrInstSrc *ordering;
-};
-
-struct IrInstGenAtomicRmw {
- IrInstGen base;
-
- IrInstGen *ptr;
- IrInstGen *operand;
- AtomicRmwOp op;
- AtomicOrder ordering;
-};
-
-struct IrInstSrcAtomicLoad {
- IrInstSrc base;
-
- IrInstSrc *operand_type;
- IrInstSrc *ptr;
- IrInstSrc *ordering;
-};
-
-struct IrInstGenAtomicLoad {
- IrInstGen base;
-
- IrInstGen *ptr;
- AtomicOrder ordering;
-};
-
-struct IrInstSrcAtomicStore {
- IrInstSrc base;
-
- IrInstSrc *operand_type;
- IrInstSrc *ptr;
- IrInstSrc *value;
- IrInstSrc *ordering;
-};
-
-struct IrInstGenAtomicStore {
- IrInstGen base;
-
- IrInstGen *ptr;
- IrInstGen *value;
- AtomicOrder ordering;
-};
-
-struct IrInstSrcSaveErrRetAddr {
- IrInstSrc base;
-};
-
-struct IrInstGenSaveErrRetAddr {
- IrInstGen base;
-};
-
-struct IrInstSrcAddImplicitReturnType {
- IrInstSrc base;
-
- IrInstSrc *value;
- ResultLocReturn *result_loc_ret;
-};
-
-// For float ops that take a single argument
-struct IrInstSrcFloatOp {
- IrInstSrc base;
-
- IrInstSrc *operand;
- BuiltinFnId fn_id;
-};
-
-struct IrInstGenFloatOp {
- IrInstGen base;
-
- IrInstGen *operand;
- BuiltinFnId fn_id;
-};
-
-struct IrInstSrcCheckRuntimeScope {
- IrInstSrc base;
-
- IrInstSrc *scope_is_comptime;
- IrInstSrc *is_comptime;
-};
-
-struct IrInstSrcBswap {
- IrInstSrc base;
-
- IrInstSrc *type;
- IrInstSrc *op;
-};
-
-struct IrInstGenBswap {
- IrInstGen base;
-
- IrInstGen *op;
-};
-
-struct IrInstSrcBitReverse {
- IrInstSrc base;
-
- IrInstSrc *type;
- IrInstSrc *op;
-};
-
-struct IrInstGenBitReverse {
- IrInstGen base;
-
- IrInstGen *op;
-};
-
-struct IrInstGenArrayToVector {
- IrInstGen base;
-
- IrInstGen *array;
-};
-
-struct IrInstGenVectorToArray {
- IrInstGen base;
-
- IrInstGen *vector;
- IrInstGen *result_loc;
-};
-
-struct IrInstSrcShuffleVector {
- IrInstSrc base;
-
- IrInstSrc *scalar_type;
- IrInstSrc *a;
- IrInstSrc *b;
- IrInstSrc *mask; // This is in zig-format, not llvm format
-};
-
-struct IrInstGenShuffleVector {
- IrInstGen base;
-
- IrInstGen *a;
- IrInstGen *b;
- IrInstGen *mask; // This is in zig-format, not llvm format
-};
-
-struct IrInstSrcSplat {
- IrInstSrc base;
-
- IrInstSrc *len;
- IrInstSrc *scalar;
-};
-
-struct IrInstGenSplat {
- IrInstGen base;
-
- IrInstGen *scalar;
-};
-
-struct IrInstGenAssertZero {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstGenAssertNonNull {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstSrcUnionInitNamedField {
- IrInstSrc base;
-
- IrInstSrc *union_type;
- IrInstSrc *field_name;
- IrInstSrc *field_result_loc;
- IrInstSrc *result_loc;
-};
-
-struct IrInstSrcHasDecl {
- IrInstSrc base;
-
- IrInstSrc *container;
- IrInstSrc *name;
-};
-
-struct IrInstSrcUndeclaredIdent {
- IrInstSrc base;
-
- Buf *name;
-};
-
-struct IrInstSrcAlloca {
- IrInstSrc base;
-
- IrInstSrc *align;
- IrInstSrc *is_comptime;
- const char *name_hint;
-};
-
-struct IrInstGenAlloca {
- IrInstGen base;
-
- uint32_t align;
- const char *name_hint;
- size_t field_index;
-};
-
-struct IrInstSrcEndExpr {
- IrInstSrc base;
-
- IrInstSrc *value;
- ResultLoc *result_loc;
-};
-
-// This one is for writing through the result pointer.
-struct IrInstSrcResolveResult {
- IrInstSrc base;
-
- ResultLoc *result_loc;
- IrInstSrc *ty;
-};
-
-struct IrInstSrcResetResult {
- IrInstSrc base;
-
- ResultLoc *result_loc;
-};
-
-struct IrInstGenPtrOfArrayToSlice {
- IrInstGen base;
-
- IrInstGen *operand;
- IrInstGen *result_loc;
-};
-
-struct IrInstSrcSuspendBegin {
- IrInstSrc base;
-};
-
-struct IrInstGenSuspendBegin {
- IrInstGen base;
-
- LLVMBasicBlockRef resume_bb;
-};
-
-struct IrInstSrcSuspendFinish {
- IrInstSrc base;
-
- IrInstSrcSuspendBegin *begin;
-};
-
-struct IrInstGenSuspendFinish {
- IrInstGen base;
-
- IrInstGenSuspendBegin *begin;
-};
-
-struct IrInstSrcAwait {
- IrInstSrc base;
-
- IrInstSrc *frame;
- ResultLoc *result_loc;
- bool is_nosuspend;
-};
-
-struct IrInstGenAwait {
- IrInstGen base;
-
- IrInstGen *frame;
- IrInstGen *result_loc;
- ZigFn *target_fn;
- bool is_nosuspend;
-};
-
-struct IrInstSrcResume {
- IrInstSrc base;
-
- IrInstSrc *frame;
-};
-
-struct IrInstGenResume {
- IrInstGen base;
-
- IrInstGen *frame;
-};
-
-enum SpillId {
- SpillIdInvalid,
- SpillIdRetErrCode,
-};
-
-struct IrInstSrcSpillBegin {
- IrInstSrc base;
-
- IrInstSrc *operand;
- SpillId spill_id;
-};
-
-struct IrInstGenSpillBegin {
- IrInstGen base;
-
- SpillId spill_id;
- IrInstGen *operand;
-};
-
-struct IrInstSrcSpillEnd {
- IrInstSrc base;
-
- IrInstSrcSpillBegin *begin;
-};
-
-struct IrInstGenSpillEnd {
- IrInstGen base;
-
- IrInstGenSpillBegin *begin;
-};
-
-struct IrInstGenVectorExtractElem {
- IrInstGen base;
-
- IrInstGen *vector;
- IrInstGen *index;
-};
-
-enum ResultLocId {
- ResultLocIdInvalid,
- ResultLocIdNone,
- ResultLocIdVar,
- ResultLocIdReturn,
- ResultLocIdPeer,
- ResultLocIdPeerParent,
- ResultLocIdInstruction,
- ResultLocIdBitCast,
- ResultLocIdCast,
-};
-
-// Additions to this struct may need to be handled in
-// ir_reset_result
-struct ResultLoc {
- ResultLocId id;
- bool written;
- bool allow_write_through_const;
- IrInstGen *resolved_loc; // result ptr
- IrInstSrc *source_instruction;
- IrInstGen *gen_instruction; // value to store to the result loc
- ZigType *implicit_elem_type;
-};
-
-struct ResultLocNone {
- ResultLoc base;
-};
-
-struct ResultLocVar {
- ResultLoc base;
-
- ZigVar *var;
-};
-
-struct ResultLocReturn {
- ResultLoc base;
-
- bool implicit_return_type_done;
-};
-
-struct IrSuspendPosition {
- size_t basic_block_index;
- size_t instruction_index;
-};
-
-struct ResultLocPeerParent {
- ResultLoc base;
-
- bool skipped;
- bool done_resuming;
- IrBasicBlockSrc *end_bb;
- ResultLoc *parent;
- ZigList peers;
- ZigType *resolved_type;
- IrInstSrc *is_comptime;
-};
-
-struct ResultLocPeer {
- ResultLoc base;
-
- ResultLocPeerParent *parent;
- IrBasicBlockSrc *next_bb;
- IrSuspendPosition suspend_pos;
-};
-
-// The result location is the source instruction
-struct ResultLocInstruction {
- ResultLoc base;
-};
-
-// The source_instruction is the destination type
-struct ResultLocBitCast {
- ResultLoc base;
-
- ResultLoc *parent;
-};
-
-// The source_instruction is the destination type
-struct ResultLocCast {
- ResultLoc base;
-
- ResultLoc *parent;
-};
-
-static const size_t slice_ptr_index = 0;
-static const size_t slice_len_index = 1;
-
-static const size_t maybe_child_index = 0;
-static const size_t maybe_null_index = 1;
-
-static const size_t err_union_payload_index = 0;
-static const size_t err_union_err_index = 1;
-
-// label (grep this): [fn_frame_struct_layout]
-static const size_t frame_fn_ptr_index = 0;
-static const size_t frame_resume_index = 1;
-static const size_t frame_awaiter_index = 2;
-static const size_t frame_ret_start = 3;
-
-// TODO https://github.com/ziglang/zig/issues/3056
-// We require this to be a power of 2 so that we can use shifting rather than
-// remainder division.
-static const size_t stack_trace_ptr_count = 32; // Must be a power of 2.
-
-#define NAMESPACE_SEP_CHAR '.'
-#define NAMESPACE_SEP_STR "."
-
-#define CACHE_OUT_SUBDIR "o"
-#define CACHE_HASH_SUBDIR "h"
-
-enum FloatMode {
- FloatModeStrict,
- FloatModeOptimized,
-};
-
-enum FnWalkId {
- FnWalkIdAttrs,
- FnWalkIdCall,
- FnWalkIdTypes,
- FnWalkIdVars,
- FnWalkIdInits,
-};
-
-struct FnWalkAttrs {
- ZigFn *fn;
- LLVMValueRef llvm_fn;
- unsigned gen_i;
-};
-
-struct FnWalkCall {
- ZigList *gen_param_values;
- ZigList *gen_param_types;
- IrInstGenCall *inst;
- bool is_var_args;
-};
-
-struct FnWalkTypes {
- ZigList *param_di_types;
- ZigList *gen_param_types;
-};
-
-struct FnWalkVars {
- ZigType *import;
- LLVMValueRef llvm_fn;
- ZigFn *fn;
- ZigVar *var;
- unsigned gen_i;
-};
-
-struct FnWalkInits {
- LLVMValueRef llvm_fn;
- ZigFn *fn;
- unsigned gen_i;
-};
-
-struct FnWalk {
- FnWalkId id;
- union {
- FnWalkAttrs attrs;
- FnWalkCall call;
- FnWalkTypes types;
- FnWalkVars vars;
- FnWalkInits inits;
- } data;
-};
-
-#endif
diff --git a/src/analyze.cpp b/src/analyze.cpp
deleted file mode 100644
index 3ba4fd79288505657e6fce4900bd0e407fe76563..0000000000000000000000000000000000000000
--- a/src/analyze.cpp
+++ /dev/null
@@ -1,9967 +0,0 @@
-/*
- * Copyright (c) 2015 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#include "analyze.hpp"
-#include "ast_render.hpp"
-#include "codegen.hpp"
-#include "config.h"
-#include "error.hpp"
-#include "ir.hpp"
-#include "ir_print.hpp"
-#include "os.hpp"
-#include "parser.hpp"
-#include "softfloat.hpp"
-#include "zig_llvm.h"
-
-
-static const size_t default_backward_branch_quota = 1000;
-
-static Error ATTRIBUTE_MUST_USE resolve_struct_type(CodeGen *g, ZigType *struct_type);
-
-static Error ATTRIBUTE_MUST_USE resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type);
-static Error ATTRIBUTE_MUST_USE resolve_struct_alignment(CodeGen *g, ZigType *struct_type);
-static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type);
-static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *union_type);
-static Error ATTRIBUTE_MUST_USE resolve_union_alignment(CodeGen *g, ZigType *union_type);
-static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry);
-static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status);
-static void preview_use_decl(CodeGen *g, TldUsingNamespace *using_namespace, ScopeDecls *dest_decls_scope);
-static void resolve_use_decl(CodeGen *g, TldUsingNamespace *tld_using_namespace, ScopeDecls *dest_decls_scope);
-static void analyze_fn_async(CodeGen *g, ZigFn *fn, bool resolve_frame);
-
-// nullptr means not analyzed yet; this one means currently being analyzed
-static const AstNode *inferred_async_checking = reinterpret_cast(0x1);
-// this one means analyzed and it's not async
-static const AstNode *inferred_async_none = reinterpret_cast(0x2);
-
-static bool is_top_level_struct(ZigType *import) {
- return import->id == ZigTypeIdStruct && import->data.structure.root_struct != nullptr;
-}
-
-static ErrorMsg *add_error_note_token(CodeGen *g, ErrorMsg *parent_msg, ZigType *owner, Token *token, Buf *msg) {
- assert(is_top_level_struct(owner));
- RootStruct *root_struct = owner->data.structure.root_struct;
-
- ErrorMsg *err = err_msg_create_with_line(root_struct->path, token->start_line, token->start_column,
- root_struct->source_code, root_struct->line_offsets, msg);
-
- err_msg_add_note(parent_msg, err);
- return err;
-}
-
-ErrorMsg *add_token_error(CodeGen *g, ZigType *owner, Token *token, Buf *msg) {
- assert(is_top_level_struct(owner));
- RootStruct *root_struct = owner->data.structure.root_struct;
- ErrorMsg *err = err_msg_create_with_line(root_struct->path, token->start_line, token->start_column,
- root_struct->source_code, root_struct->line_offsets, msg);
-
- g->errors.append(err);
- g->trace_err = err;
- return err;
-}
-
-ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
- Token fake_token;
- fake_token.start_line = node->line;
- fake_token.start_column = node->column;
- node->already_traced_this_node = true;
- return add_token_error(g, node->owner, &fake_token, msg);
-}
-
-ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, const AstNode *node, Buf *msg) {
- Token fake_token;
- fake_token.start_line = node->line;
- fake_token.start_column = node->column;
- return add_error_note_token(g, parent_msg, node->owner, &fake_token, msg);
-}
-
-ZigType *new_type_table_entry(ZigTypeId id) {
- ZigType *entry = heap::c_allocator.create();
- entry->id = id;
- return entry;
-}
-
-static ScopeDecls **get_container_scope_ptr(ZigType *type_entry) {
- if (type_entry->id == ZigTypeIdStruct) {
- return &type_entry->data.structure.decls_scope;
- } else if (type_entry->id == ZigTypeIdEnum) {
- return &type_entry->data.enumeration.decls_scope;
- } else if (type_entry->id == ZigTypeIdUnion) {
- return &type_entry->data.unionation.decls_scope;
- }
- zig_unreachable();
-}
-
-static ScopeExpr *find_expr_scope(Scope *scope) {
- for (;;) {
- switch (scope->id) {
- case ScopeIdExpr:
- return reinterpret_cast(scope);
- case ScopeIdDefer:
- case ScopeIdDeferExpr:
- case ScopeIdDecls:
- case ScopeIdFnDef:
- case ScopeIdCompTime:
- case ScopeIdNoSuspend:
- case ScopeIdVarDecl:
- case ScopeIdCImport:
- case ScopeIdSuspend:
- case ScopeIdTypeOf:
- case ScopeIdBlock:
- return nullptr;
- case ScopeIdLoop:
- case ScopeIdRuntime:
- scope = scope->parent;
- continue;
- }
- }
-}
-
-static void update_progress_display(CodeGen *g) {
- stage2_progress_update_node(g->sub_progress_node,
- g->resolve_queue_index + g->fn_defs_index,
- g->resolve_queue.length + g->fn_defs.length);
-}
-
-ScopeDecls *get_container_scope(ZigType *type_entry) {
- return *get_container_scope_ptr(type_entry);
-}
-
-void init_scope(CodeGen *g, Scope *dest, ScopeId id, AstNode *source_node, Scope *parent) {
- dest->codegen = g;
- dest->id = id;
- dest->source_node = source_node;
- dest->parent = parent;
-}
-
-ScopeDecls *create_decls_scope(CodeGen *g, AstNode *node, Scope *parent, ZigType *container_type,
- ZigType *import, Buf *bare_name)
-{
- ScopeDecls *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdDecls, node, parent);
- scope->decl_table.init(4);
- scope->container_type = container_type;
- scope->import = import;
- scope->bare_name = bare_name;
- return scope;
-}
-
-ScopeBlock *create_block_scope(CodeGen *g, AstNode *node, Scope *parent) {
- assert(node->type == NodeTypeBlock);
- ScopeBlock *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdBlock, node, parent);
- scope->name = node->data.block.name;
- return scope;
-}
-
-ScopeDefer *create_defer_scope(CodeGen *g, AstNode *node, Scope *parent) {
- assert(node->type == NodeTypeDefer);
- ScopeDefer *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdDefer, node, parent);
- return scope;
-}
-
-ScopeDeferExpr *create_defer_expr_scope(CodeGen *g, AstNode *node, Scope *parent) {
- assert(node->type == NodeTypeDefer);
- ScopeDeferExpr *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdDeferExpr, node, parent);
- return scope;
-}
-
-Scope *create_var_scope(CodeGen *g, AstNode *node, Scope *parent, ZigVar *var) {
- ScopeVarDecl *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdVarDecl, node, parent);
- scope->var = var;
- return &scope->base;
-}
-
-ScopeCImport *create_cimport_scope(CodeGen *g, AstNode *node, Scope *parent) {
- assert(node->type == NodeTypeFnCallExpr);
- ScopeCImport *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdCImport, node, parent);
- buf_resize(&scope->buf, 0);
- return scope;
-}
-
-ScopeLoop *create_loop_scope(CodeGen *g, AstNode *node, Scope *parent) {
- ScopeLoop *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdLoop, node, parent);
- if (node->type == NodeTypeWhileExpr) {
- scope->name = node->data.while_expr.name;
- } else if (node->type == NodeTypeForExpr) {
- scope->name = node->data.for_expr.name;
- } else {
- zig_unreachable();
- }
- return scope;
-}
-
-Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstSrc *is_comptime) {
- ScopeRuntime *scope = heap::c_allocator.create();
- scope->is_comptime = is_comptime;
- init_scope(g, &scope->base, ScopeIdRuntime, node, parent);
- return &scope->base;
-}
-
-ScopeSuspend *create_suspend_scope(CodeGen *g, AstNode *node, Scope *parent) {
- assert(node->type == NodeTypeSuspend);
- ScopeSuspend *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdSuspend, node, parent);
- return scope;
-}
-
-ScopeFnDef *create_fndef_scope(CodeGen *g, AstNode *node, Scope *parent, ZigFn *fn_entry) {
- ScopeFnDef *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdFnDef, node, parent);
- scope->fn_entry = fn_entry;
- return scope;
-}
-
-Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent) {
- ScopeCompTime *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdCompTime, node, parent);
- return &scope->base;
-}
-
-Scope *create_nosuspend_scope(CodeGen *g, AstNode *node, Scope *parent) {
- ScopeNoSuspend *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdNoSuspend, node, parent);
- return &scope->base;
-}
-
-Scope *create_typeof_scope(CodeGen *g, AstNode *node, Scope *parent) {
- ScopeTypeOf *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdTypeOf, node, parent);
- return &scope->base;
-}
-
-ScopeExpr *create_expr_scope(CodeGen *g, AstNode *node, Scope *parent) {
- ScopeExpr *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdExpr, node, parent);
- ScopeExpr *parent_expr = find_expr_scope(parent);
- if (parent_expr != nullptr) {
- size_t new_len = parent_expr->children_len + 1;
- parent_expr->children_ptr = heap::c_allocator.reallocate_nonzero(
- parent_expr->children_ptr, parent_expr->children_len, new_len);
- parent_expr->children_ptr[parent_expr->children_len] = scope;
- parent_expr->children_len = new_len;
- }
- return scope;
-}
-
-ZigType *get_scope_import(Scope *scope) {
- while (scope) {
- if (scope->id == ScopeIdDecls) {
- ScopeDecls *decls_scope = (ScopeDecls *)scope;
- assert(is_top_level_struct(decls_scope->import));
- return decls_scope->import;
- }
- scope = scope->parent;
- }
- zig_unreachable();
-}
-
-ScopeTypeOf *get_scope_typeof(Scope *scope) {
- while (scope) {
- switch (scope->id) {
- case ScopeIdTypeOf:
- return reinterpret_cast(scope);
- case ScopeIdFnDef:
- case ScopeIdDecls:
- return nullptr;
- default:
- scope = scope->parent;
- continue;
- }
- }
- zig_unreachable();
-}
-
-static ZigType *new_container_type_entry(CodeGen *g, ZigTypeId id, AstNode *source_node, Scope *parent_scope,
- Buf *bare_name)
-{
- ZigType *entry = new_type_table_entry(id);
- *get_container_scope_ptr(entry) = create_decls_scope(g, source_node, parent_scope, entry,
- get_scope_import(parent_scope), bare_name);
- return entry;
-}
-
-static uint8_t bits_needed_for_unsigned(uint64_t x) {
- if (x == 0) {
- return 0;
- }
- uint8_t base = log2_u64(x);
- uint64_t upper = (((uint64_t)1) << base) - 1;
- return (upper >= x) ? base : (base + 1);
-}
-
-AstNode *type_decl_node(ZigType *type_entry) {
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdStruct:
- return type_entry->data.structure.decl_node;
- case ZigTypeIdEnum:
- return type_entry->data.enumeration.decl_node;
- case ZigTypeIdUnion:
- return type_entry->data.unionation.decl_node;
- case ZigTypeIdFnFrame:
- return type_entry->data.frame.fn->proto_node;
- case ZigTypeIdOpaque:
- case ZigTypeIdMetaType:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdUnreachable:
- case ZigTypeIdInt:
- case ZigTypeIdFloat:
- case ZigTypeIdPointer:
- case ZigTypeIdArray:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdOptional:
- case ZigTypeIdErrorUnion:
- case ZigTypeIdErrorSet:
- case ZigTypeIdFn:
- case ZigTypeIdBoundFn:
- case ZigTypeIdVector:
- case ZigTypeIdAnyFrame:
- return nullptr;
- }
- zig_unreachable();
-}
-
-bool type_is_resolved(ZigType *type_entry, ResolveStatus status) {
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdStruct:
- return type_entry->data.structure.resolve_status >= status;
- case ZigTypeIdUnion:
- return type_entry->data.unionation.resolve_status >= status;
- case ZigTypeIdEnum:
- return type_entry->data.enumeration.resolve_status >= status;
- case ZigTypeIdFnFrame:
- switch (status) {
- case ResolveStatusInvalid:
- zig_unreachable();
- case ResolveStatusBeingInferred:
- zig_unreachable();
- case ResolveStatusUnstarted:
- case ResolveStatusZeroBitsKnown:
- return true;
- case ResolveStatusAlignmentKnown:
- case ResolveStatusSizeKnown:
- return type_entry->data.frame.locals_struct != nullptr;
- case ResolveStatusLLVMFwdDecl:
- case ResolveStatusLLVMFull:
- return type_entry->llvm_type != nullptr;
- }
- zig_unreachable();
- case ZigTypeIdOpaque:
- return status < ResolveStatusSizeKnown;
- case ZigTypeIdPointer:
- switch (status) {
- case ResolveStatusInvalid:
- zig_unreachable();
- case ResolveStatusBeingInferred:
- zig_unreachable();
- case ResolveStatusUnstarted:
- return true;
- case ResolveStatusZeroBitsKnown:
- case ResolveStatusAlignmentKnown:
- case ResolveStatusSizeKnown:
- return type_entry->abi_size != SIZE_MAX;
- case ResolveStatusLLVMFwdDecl:
- case ResolveStatusLLVMFull:
- return type_entry->llvm_type != nullptr;
- }
- zig_unreachable();
- case ZigTypeIdMetaType:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdUnreachable:
- case ZigTypeIdInt:
- case ZigTypeIdFloat:
- case ZigTypeIdArray:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdOptional:
- case ZigTypeIdErrorUnion:
- case ZigTypeIdErrorSet:
- case ZigTypeIdFn:
- case ZigTypeIdBoundFn:
- case ZigTypeIdVector:
- case ZigTypeIdAnyFrame:
- return true;
- }
- zig_unreachable();
-}
-
-bool type_is_complete(ZigType *type_entry) {
- return type_is_resolved(type_entry, ResolveStatusSizeKnown);
-}
-
-uint64_t type_size(CodeGen *g, ZigType *type_entry) {
- assert(type_is_resolved(type_entry, ResolveStatusSizeKnown));
- return type_entry->abi_size;
-}
-
-uint64_t type_size_bits(CodeGen *g, ZigType *type_entry) {
- assert(type_is_resolved(type_entry, ResolveStatusSizeKnown));
- return type_entry->size_in_bits;
-}
-
-uint32_t get_abi_alignment(CodeGen *g, ZigType *type_entry) {
- assert(type_is_resolved(type_entry, ResolveStatusAlignmentKnown));
- return type_entry->abi_align;
-}
-
-static bool is_slice(ZigType *type) {
- return type->id == ZigTypeIdStruct && type->data.structure.special == StructSpecialSlice;
-}
-
-ZigType *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) {
- return get_int_type(g, false, bits_needed_for_unsigned(x));
-}
-
-ZigType *get_any_frame_type(CodeGen *g, ZigType *result_type) {
- if (result_type != nullptr && result_type->any_frame_parent != nullptr) {
- return result_type->any_frame_parent;
- } else if (result_type == nullptr && g->builtin_types.entry_any_frame != nullptr) {
- return g->builtin_types.entry_any_frame;
- }
-
- ZigType *entry = new_type_table_entry(ZigTypeIdAnyFrame);
- entry->abi_size = g->builtin_types.entry_usize->abi_size;
- entry->size_in_bits = g->builtin_types.entry_usize->size_in_bits;
- entry->abi_align = g->builtin_types.entry_usize->abi_align;
- entry->data.any_frame.result_type = result_type;
- buf_init_from_str(&entry->name, "anyframe");
- if (result_type != nullptr) {
- buf_appendf(&entry->name, "->%s", buf_ptr(&result_type->name));
- }
-
- if (result_type != nullptr) {
- result_type->any_frame_parent = entry;
- } else if (result_type == nullptr) {
- g->builtin_types.entry_any_frame = entry;
- }
- return entry;
-}
-
-ZigType *get_fn_frame_type(CodeGen *g, ZigFn *fn) {
- if (fn->frame_type != nullptr) {
- return fn->frame_type;
- }
-
- ZigType *entry = new_type_table_entry(ZigTypeIdFnFrame);
- buf_resize(&entry->name, 0);
- buf_appendf(&entry->name, "@Frame(%s)", buf_ptr(&fn->symbol_name));
-
- entry->data.frame.fn = fn;
-
- // Async function frames are always non-zero bits because they always have a resume index.
- entry->abi_size = SIZE_MAX;
- entry->size_in_bits = SIZE_MAX;
-
- fn->frame_type = entry;
- return entry;
-}
-
-static void append_ptr_type_attrs(Buf *type_name, ZigType *ptr_type) {
- const char *const_str = ptr_type->data.pointer.is_const ? "const " : "";
- const char *volatile_str = ptr_type->data.pointer.is_volatile ? "volatile " : "";
- const char *allow_zero_str;
- if (ptr_type->data.pointer.ptr_len == PtrLenC) {
- assert(ptr_type->data.pointer.allow_zero);
- allow_zero_str = "";
- } else {
- allow_zero_str = ptr_type->data.pointer.allow_zero ? "allowzero " : "";
- }
- if (ptr_type->data.pointer.explicit_alignment != 0 || ptr_type->data.pointer.host_int_bytes != 0 ||
- ptr_type->data.pointer.vector_index != VECTOR_INDEX_NONE)
- {
- buf_appendf(type_name, "align(");
- if (ptr_type->data.pointer.explicit_alignment != 0) {
- buf_appendf(type_name, "%" PRIu32, ptr_type->data.pointer.explicit_alignment);
- }
- if (ptr_type->data.pointer.host_int_bytes != 0) {
- buf_appendf(type_name, ":%" PRIu32 ":%" PRIu32, ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes);
- }
- if (ptr_type->data.pointer.vector_index == VECTOR_INDEX_RUNTIME) {
- buf_appendf(type_name, ":?");
- } else if (ptr_type->data.pointer.vector_index != VECTOR_INDEX_NONE) {
- buf_appendf(type_name, ":%" PRIu32, ptr_type->data.pointer.vector_index);
- }
- buf_appendf(type_name, ") ");
- }
- buf_appendf(type_name, "%s%s%s", const_str, volatile_str, allow_zero_str);
- if (ptr_type->data.pointer.inferred_struct_field != nullptr) {
- buf_appendf(type_name, " field '%s' of %s)",
- buf_ptr(ptr_type->data.pointer.inferred_struct_field->field_name),
- buf_ptr(&ptr_type->data.pointer.inferred_struct_field->inferred_struct_type->name));
- } else {
- buf_appendf(type_name, "%s", buf_ptr(&ptr_type->data.pointer.child_type->name));
- }
-}
-
-ZigType *get_pointer_to_type_extra2(CodeGen *g, ZigType *child_type, bool is_const,
- bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment,
- uint32_t bit_offset_in_host, uint32_t host_int_bytes, bool allow_zero,
- uint32_t vector_index, InferredStructField *inferred_struct_field, ZigValue *sentinel)
-{
- assert(ptr_len != PtrLenC || allow_zero);
- assert(!type_is_invalid(child_type));
- assert(ptr_len == PtrLenSingle || child_type->id != ZigTypeIdOpaque);
-
- if (byte_alignment != 0) {
- uint32_t abi_alignment = get_abi_alignment(g, child_type);
- if (byte_alignment == abi_alignment)
- byte_alignment = 0;
- }
-
- if (host_int_bytes != 0 && vector_index == VECTOR_INDEX_NONE) {
- uint32_t child_type_bits = type_size_bits(g, child_type);
- if (host_int_bytes * 8 == child_type_bits) {
- assert(bit_offset_in_host == 0);
- host_int_bytes = 0;
- }
- }
-
- TypeId type_id = {};
- ZigType **parent_pointer = nullptr;
- if (host_int_bytes != 0 || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle ||
- allow_zero || vector_index != VECTOR_INDEX_NONE || inferred_struct_field != nullptr ||
- sentinel != nullptr)
- {
- type_id.id = ZigTypeIdPointer;
- type_id.data.pointer.codegen = g;
- type_id.data.pointer.child_type = child_type;
- type_id.data.pointer.is_const = is_const;
- type_id.data.pointer.is_volatile = is_volatile;
- type_id.data.pointer.alignment = byte_alignment;
- type_id.data.pointer.bit_offset_in_host = bit_offset_in_host;
- type_id.data.pointer.host_int_bytes = host_int_bytes;
- type_id.data.pointer.ptr_len = ptr_len;
- type_id.data.pointer.allow_zero = allow_zero;
- type_id.data.pointer.vector_index = vector_index;
- type_id.data.pointer.inferred_struct_field = inferred_struct_field;
- type_id.data.pointer.sentinel = sentinel;
-
- auto existing_entry = g->type_table.maybe_get(type_id);
- if (existing_entry)
- return existing_entry->value;
- } else {
- assert(bit_offset_in_host == 0);
- parent_pointer = &child_type->pointer_parent[(is_const ? 1 : 0)];
- if (*parent_pointer) {
- assert((*parent_pointer)->data.pointer.explicit_alignment == 0);
- return *parent_pointer;
- }
- }
-
- ZigType *entry = new_type_table_entry(ZigTypeIdPointer);
-
- buf_resize(&entry->name, 0);
- if (inferred_struct_field != nullptr) {
- buf_appendf(&entry->name, "(");
- }
- switch (ptr_len) {
- case PtrLenSingle:
- assert(sentinel == nullptr);
- buf_appendf(&entry->name, "*");
- break;
- case PtrLenUnknown:
- buf_appendf(&entry->name, "[*");
- break;
- case PtrLenC:
- assert(sentinel == nullptr);
- buf_appendf(&entry->name, "[*c]");
- break;
- }
- if (sentinel != nullptr) {
- buf_appendf(&entry->name, ":");
- render_const_value(g, &entry->name, sentinel);
- }
- switch (ptr_len) {
- case PtrLenSingle:
- case PtrLenC:
- break;
- case PtrLenUnknown:
- buf_appendf(&entry->name, "]");
- break;
- }
-
- if (inferred_struct_field != nullptr) {
- entry->abi_size = SIZE_MAX;
- entry->size_in_bits = SIZE_MAX;
- entry->abi_align = UINT32_MAX;
- } else if (type_is_resolved(child_type, ResolveStatusZeroBitsKnown)) {
- if (type_has_bits(g, child_type)) {
- entry->abi_size = g->builtin_types.entry_usize->abi_size;
- entry->size_in_bits = g->builtin_types.entry_usize->size_in_bits;
- entry->abi_align = g->builtin_types.entry_usize->abi_align;
- } else {
- assert(byte_alignment == 0);
- entry->abi_size = 0;
- entry->size_in_bits = 0;
- entry->abi_align = 0;
- }
- } else {
- entry->abi_size = SIZE_MAX;
- entry->size_in_bits = SIZE_MAX;
- entry->abi_align = UINT32_MAX;
- }
-
- entry->data.pointer.ptr_len = ptr_len;
- entry->data.pointer.child_type = child_type;
- entry->data.pointer.is_const = is_const;
- entry->data.pointer.is_volatile = is_volatile;
- entry->data.pointer.explicit_alignment = byte_alignment;
- entry->data.pointer.bit_offset_in_host = bit_offset_in_host;
- entry->data.pointer.host_int_bytes = host_int_bytes;
- entry->data.pointer.allow_zero = allow_zero;
- entry->data.pointer.vector_index = vector_index;
- entry->data.pointer.inferred_struct_field = inferred_struct_field;
- entry->data.pointer.sentinel = sentinel;
-
- append_ptr_type_attrs(&entry->name, entry);
-
- if (parent_pointer) {
- *parent_pointer = entry;
- } else {
- g->type_table.put(type_id, entry);
- }
- return entry;
-}
-
-ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const,
- bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment,
- uint32_t bit_offset_in_host, uint32_t host_int_bytes, bool allow_zero)
-{
- return get_pointer_to_type_extra2(g, child_type, is_const, is_volatile, ptr_len,
- byte_alignment, bit_offset_in_host, host_int_bytes, allow_zero, VECTOR_INDEX_NONE, nullptr, nullptr);
-}
-
-ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const) {
- return get_pointer_to_type_extra2(g, child_type, is_const, false, PtrLenSingle, 0, 0, 0, false,
- VECTOR_INDEX_NONE, nullptr, nullptr);
-}
-
-ZigType *get_optional_type(CodeGen *g, ZigType *child_type) {
- ZigType *result = get_optional_type2(g, child_type);
- if (result == nullptr) {
- codegen_report_errors_and_exit(g);
- }
- return result;
-}
-
-ZigType *get_optional_type2(CodeGen *g, ZigType *child_type) {
- if (child_type->optional_parent != nullptr) {
- return child_type->optional_parent;
- }
-
- Error err;
- if ((err = type_resolve(g, child_type, ResolveStatusSizeKnown))) {
- return nullptr;
- }
-
- ZigType *entry = new_type_table_entry(ZigTypeIdOptional);
-
- buf_resize(&entry->name, 0);
- buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name));
-
- if (!type_has_bits(g, child_type)) {
- entry->size_in_bits = g->builtin_types.entry_bool->size_in_bits;
- entry->abi_size = g->builtin_types.entry_bool->abi_size;
- entry->abi_align = g->builtin_types.entry_bool->abi_align;
- } else if (type_is_nonnull_ptr(g, child_type) || child_type->id == ZigTypeIdErrorSet) {
- // This is an optimization but also is necessary for calling C
- // functions where all pointers are optional pointers.
- // Function types are technically pointers.
- entry->size_in_bits = child_type->size_in_bits;
- entry->abi_size = child_type->abi_size;
- entry->abi_align = child_type->abi_align;
- } else {
- // This value only matters if the type is legal in a packed struct, which is not
- // true for optional types which did not fit the above 2 categories (zero bit child type,
- // or nonnull ptr child type, or error set child type).
- entry->size_in_bits = child_type->size_in_bits + 1;
-
- // We're going to make a struct with the child type as the first field,
- // and a bool as the second. Since the child type's abi alignment is guaranteed
- // to be >= the bool's abi size (1 byte), the added size is exactly equal to the
- // child type's ABI alignment.
- assert(child_type->abi_align >= g->builtin_types.entry_bool->abi_size);
- entry->abi_align = child_type->abi_align;
- entry->abi_size = child_type->abi_size + child_type->abi_align;
- }
-
- entry->data.maybe.child_type = child_type;
- entry->data.maybe.resolve_status = ResolveStatusSizeKnown;
-
- child_type->optional_parent = entry;
- return entry;
-}
-
-static size_t align_forward(size_t addr, size_t alignment) {
- return (addr + alignment - 1) & ~(alignment - 1);
-}
-
-static size_t next_field_offset(size_t offset, size_t align_from_zero, size_t field_size, size_t next_field_align) {
- // Convert offset to a pretend address which has the specified alignment.
- size_t addr = offset + align_from_zero;
- // March the address forward to respect the field alignment.
- size_t aligned_addr = align_forward(addr + field_size, next_field_align);
- // Convert back from pretend address to offset.
- return aligned_addr - align_from_zero;
-}
-
-ZigType *get_error_union_type(CodeGen *g, ZigType *err_set_type, ZigType *payload_type) {
- assert(err_set_type->id == ZigTypeIdErrorSet);
- assert(!type_is_invalid(payload_type));
-
- TypeId type_id = {};
- type_id.id = ZigTypeIdErrorUnion;
- type_id.data.error_union.err_set_type = err_set_type;
- type_id.data.error_union.payload_type = payload_type;
-
- auto existing_entry = g->type_table.maybe_get(type_id);
- if (existing_entry) {
- return existing_entry->value;
- }
-
- ZigType *entry = new_type_table_entry(ZigTypeIdErrorUnion);
- assert(type_is_resolved(payload_type, ResolveStatusSizeKnown));
-
- buf_resize(&entry->name, 0);
- buf_appendf(&entry->name, "%s!%s", buf_ptr(&err_set_type->name), buf_ptr(&payload_type->name));
-
- entry->data.error_union.err_set_type = err_set_type;
- entry->data.error_union.payload_type = payload_type;
-
- if (!type_has_bits(g, payload_type)) {
- if (type_has_bits(g, err_set_type)) {
- entry->size_in_bits = err_set_type->size_in_bits;
- entry->abi_size = err_set_type->abi_size;
- entry->abi_align = err_set_type->abi_align;
- } else {
- entry->size_in_bits = 0;
- entry->abi_size = 0;
- entry->abi_align = 0;
- }
- } else if (!type_has_bits(g, err_set_type)) {
- entry->size_in_bits = payload_type->size_in_bits;
- entry->abi_size = payload_type->abi_size;
- entry->abi_align = payload_type->abi_align;
- } else {
- entry->abi_align = max(err_set_type->abi_align, payload_type->abi_align);
- size_t field_sizes[2];
- size_t field_aligns[2];
- field_sizes[err_union_err_index] = err_set_type->abi_size;
- field_aligns[err_union_err_index] = err_set_type->abi_align;
- field_sizes[err_union_payload_index] = payload_type->abi_size;
- field_aligns[err_union_payload_index] = payload_type->abi_align;
- size_t field2_offset = next_field_offset(0, entry->abi_align, field_sizes[0], field_aligns[1]);
- entry->abi_size = next_field_offset(field2_offset, entry->abi_align, field_sizes[1], entry->abi_align);
- entry->size_in_bits = entry->abi_size * 8;
- entry->data.error_union.pad_bytes = entry->abi_size - (field2_offset + field_sizes[1]);
- }
-
- g->type_table.put(type_id, entry);
- return entry;
-}
-
-ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, ZigValue *sentinel) {
- Error err;
-
- TypeId type_id = {};
- type_id.id = ZigTypeIdArray;
- type_id.data.array.codegen = g;
- type_id.data.array.child_type = child_type;
- type_id.data.array.size = array_size;
- type_id.data.array.sentinel = sentinel;
- auto existing_entry = g->type_table.maybe_get(type_id);
- if (existing_entry) {
- return existing_entry->value;
- }
-
- size_t full_array_size = array_size + ((sentinel != nullptr) ? 1 : 0);
-
- if (full_array_size != 0 && (err = type_resolve(g, child_type, ResolveStatusSizeKnown))) {
- codegen_report_errors_and_exit(g);
- }
-
- ZigType *entry = new_type_table_entry(ZigTypeIdArray);
-
- buf_resize(&entry->name, 0);
- buf_appendf(&entry->name, "[%" ZIG_PRI_u64, array_size);
- if (sentinel != nullptr) {
- buf_appendf(&entry->name, ":");
- render_const_value(g, &entry->name, sentinel);
- }
- buf_appendf(&entry->name, "]%s", buf_ptr(&child_type->name));
-
- entry->size_in_bits = child_type->size_in_bits * full_array_size;
- entry->abi_align = (full_array_size == 0) ? 0 : child_type->abi_align;
- entry->abi_size = child_type->abi_size * full_array_size;
-
- entry->data.array.child_type = child_type;
- entry->data.array.len = array_size;
- entry->data.array.sentinel = sentinel;
-
- g->type_table.put(type_id, entry);
- return entry;
-}
-
-ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {
- assert(ptr_type->id == ZigTypeIdPointer);
- assert(ptr_type->data.pointer.ptr_len == PtrLenUnknown);
-
- ZigType **parent_pointer = &ptr_type->data.pointer.slice_parent;
- if (*parent_pointer) {
- return *parent_pointer;
- }
-
- ZigType *entry = new_type_table_entry(ZigTypeIdStruct);
-
- buf_resize(&entry->name, 0);
- buf_appendf(&entry->name, "[");
- if (ptr_type->data.pointer.sentinel != nullptr) {
- buf_appendf(&entry->name, ":");
- render_const_value(g, &entry->name, ptr_type->data.pointer.sentinel);
- }
- buf_appendf(&entry->name, "]");
- append_ptr_type_attrs(&entry->name, ptr_type);
-
- unsigned element_count = 2;
- Buf *ptr_field_name = buf_create_from_str("ptr");
- Buf *len_field_name = buf_create_from_str("len");
-
- entry->data.structure.resolve_status = ResolveStatusSizeKnown;
- entry->data.structure.layout = ContainerLayoutAuto;
- entry->data.structure.special = StructSpecialSlice;
- entry->data.structure.src_field_count = element_count;
- entry->data.structure.gen_field_count = element_count;
- entry->data.structure.fields = alloc_type_struct_fields(element_count);
- entry->data.structure.fields_by_name.init(element_count);
- entry->data.structure.fields[slice_ptr_index]->name = ptr_field_name;
- entry->data.structure.fields[slice_ptr_index]->type_entry = ptr_type;
- entry->data.structure.fields[slice_ptr_index]->src_index = slice_ptr_index;
- entry->data.structure.fields[slice_ptr_index]->gen_index = 0;
- entry->data.structure.fields[slice_ptr_index]->offset = 0;
- entry->data.structure.fields[slice_len_index]->name = len_field_name;
- entry->data.structure.fields[slice_len_index]->type_entry = g->builtin_types.entry_usize;
- entry->data.structure.fields[slice_len_index]->src_index = slice_len_index;
- entry->data.structure.fields[slice_len_index]->gen_index = 1;
- entry->data.structure.fields[slice_len_index]->offset = ptr_type->abi_size;
-
- entry->data.structure.fields_by_name.put(ptr_field_name, entry->data.structure.fields[slice_ptr_index]);
- entry->data.structure.fields_by_name.put(len_field_name, entry->data.structure.fields[slice_len_index]);
-
- switch (type_requires_comptime(g, ptr_type)) {
- case ReqCompTimeInvalid:
- zig_unreachable();
- case ReqCompTimeNo:
- break;
- case ReqCompTimeYes:
- entry->data.structure.requires_comptime = true;
- }
-
- if (!type_has_bits(g, ptr_type)) {
- entry->data.structure.gen_field_count = 1;
- entry->data.structure.fields[slice_ptr_index]->gen_index = SIZE_MAX;
- entry->data.structure.fields[slice_len_index]->gen_index = 0;
- }
-
- if (type_has_bits(g, ptr_type)) {
- entry->size_in_bits = ptr_type->size_in_bits + g->builtin_types.entry_usize->size_in_bits;
- entry->abi_size = ptr_type->abi_size + g->builtin_types.entry_usize->abi_size;
- entry->abi_align = ptr_type->abi_align;
- } else {
- entry->size_in_bits = g->builtin_types.entry_usize->size_in_bits;
- entry->abi_size = g->builtin_types.entry_usize->abi_size;
- entry->abi_align = g->builtin_types.entry_usize->abi_align;
- }
-
- *parent_pointer = entry;
- return entry;
-}
-
-ZigType *get_opaque_type(CodeGen *g, Scope *scope, AstNode *source_node, const char *full_name, Buf *bare_name) {
- ZigType *entry = new_type_table_entry(ZigTypeIdOpaque);
-
- buf_init_from_str(&entry->name, full_name);
-
- ZigType *import = scope ? get_scope_import(scope) : nullptr;
- unsigned line = source_node ? (unsigned)(source_node->line + 1) : 0;
-
- entry->llvm_type = LLVMInt8Type();
- entry->llvm_di_type = ZigLLVMCreateDebugForwardDeclType(g->dbuilder,
- ZigLLVMTag_DW_structure_type(), full_name,
- import ? ZigLLVMFileToScope(import->data.structure.root_struct->di_file) : nullptr,
- import ? import->data.structure.root_struct->di_file : nullptr,
- line);
- entry->data.opaque.bare_name = bare_name;
-
- // The actual size is unknown, but the value must not be 0 because that
- // is how type_has_bits is determined.
- entry->abi_size = SIZE_MAX;
- entry->size_in_bits = SIZE_MAX;
- entry->abi_align = 1;
-
- return entry;
-}
-
-ZigType *get_bound_fn_type(CodeGen *g, ZigFn *fn_entry) {
- ZigType *fn_type = fn_entry->type_entry;
- assert(fn_type->id == ZigTypeIdFn);
- if (fn_type->data.fn.bound_fn_parent)
- return fn_type->data.fn.bound_fn_parent;
-
- ZigType *bound_fn_type = new_type_table_entry(ZigTypeIdBoundFn);
- bound_fn_type->data.bound_fn.fn_type = fn_type;
-
- buf_resize(&bound_fn_type->name, 0);
- buf_appendf(&bound_fn_type->name, "(bound %s)", buf_ptr(&fn_type->name));
-
- fn_type->data.fn.bound_fn_parent = bound_fn_type;
- return bound_fn_type;
-}
-
-const char *calling_convention_name(CallingConvention cc) {
- switch (cc) {
- case CallingConventionUnspecified: return "Unspecified";
- case CallingConventionC: return "C";
- case CallingConventionCold: return "Cold";
- case CallingConventionNaked: return "Naked";
- case CallingConventionAsync: return "Async";
- case CallingConventionInterrupt: return "Interrupt";
- case CallingConventionSignal: return "Signal";
- case CallingConventionStdcall: return "Stdcall";
- case CallingConventionFastcall: return "Fastcall";
- case CallingConventionVectorcall: return "Vectorcall";
- case CallingConventionThiscall: return "Thiscall";
- case CallingConventionAPCS: return "Apcs";
- case CallingConventionAAPCS: return "Aapcs";
- case CallingConventionAAPCSVFP: return "Aapcsvfp";
- }
- zig_unreachable();
-}
-
-bool calling_convention_allows_zig_types(CallingConvention cc) {
- switch (cc) {
- case CallingConventionUnspecified:
- case CallingConventionAsync:
- return true;
- case CallingConventionC:
- case CallingConventionCold:
- case CallingConventionNaked:
- case CallingConventionInterrupt:
- case CallingConventionSignal:
- case CallingConventionStdcall:
- case CallingConventionFastcall:
- case CallingConventionVectorcall:
- case CallingConventionThiscall:
- case CallingConventionAPCS:
- case CallingConventionAAPCS:
- case CallingConventionAAPCSVFP:
- return false;
- }
- zig_unreachable();
-}
-
-ZigType *get_stack_trace_type(CodeGen *g) {
- if (g->stack_trace_type == nullptr) {
- g->stack_trace_type = get_builtin_type(g, "StackTrace");
- assertNoError(type_resolve(g, g->stack_trace_type, ResolveStatusZeroBitsKnown));
- }
- return g->stack_trace_type;
-}
-
-bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) {
- if (fn_type_id->cc == CallingConventionUnspecified) {
- return handle_is_ptr(g, fn_type_id->return_type);
- }
- if (fn_type_id->cc != CallingConventionC) {
- return false;
- }
- if (type_is_c_abi_int_bail(g, fn_type_id->return_type)) {
- return false;
- }
- if (g->zig_target->arch == ZigLLVM_x86 ||
- g->zig_target->arch == ZigLLVM_x86_64 ||
- target_is_arm(g->zig_target) ||
- target_is_riscv(g->zig_target) ||
- target_is_wasm(g->zig_target) ||
- target_is_ppc(g->zig_target))
- {
- X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type);
- return abi_class == X64CABIClass_MEMORY || abi_class == X64CABIClass_MEMORY_nobyval;
- } else if (g->zig_target->arch == ZigLLVM_mips || g->zig_target->arch == ZigLLVM_mipsel) {
- return false;
- }
- zig_panic("TODO implement C ABI for this architecture. See https://github.com/ziglang/zig/issues/1481");
-}
-
-ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
- Error err;
- auto table_entry = g->fn_type_table.maybe_get(fn_type_id);
- if (table_entry) {
- return table_entry->value;
- }
- if (fn_type_id->return_type != nullptr) {
- if ((err = type_resolve(g, fn_type_id->return_type, ResolveStatusSizeKnown)))
- return g->builtin_types.entry_invalid;
- assert(fn_type_id->return_type->id != ZigTypeIdOpaque);
- } else {
- zig_panic("TODO implement inferred return types https://github.com/ziglang/zig/issues/447");
- }
-
- ZigType *fn_type = new_type_table_entry(ZigTypeIdFn);
- fn_type->data.fn.fn_type_id = *fn_type_id;
-
- // populate the name of the type
- buf_resize(&fn_type->name, 0);
- buf_appendf(&fn_type->name, "fn(");
- for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
- FnTypeParamInfo *param_info = &fn_type_id->param_info[i];
-
- ZigType *param_type = param_info->type;
- const char *comma = (i == 0) ? "" : ", ";
- const char *noalias_str = param_info->is_noalias ? "noalias " : "";
- buf_appendf(&fn_type->name, "%s%s%s", comma, noalias_str, buf_ptr(¶m_type->name));
- }
-
- if (fn_type_id->is_var_args) {
- const char *comma = (fn_type_id->param_count == 0) ? "" : ", ";
- buf_appendf(&fn_type->name, "%s...", comma);
- }
- buf_appendf(&fn_type->name, ")");
- if (fn_type_id->alignment != 0) {
- buf_appendf(&fn_type->name, " align(%" PRIu32 ")", fn_type_id->alignment);
- }
- if (fn_type_id->cc != CallingConventionUnspecified) {
- buf_appendf(&fn_type->name, " callconv(.%s)", calling_convention_name(fn_type_id->cc));
- }
- buf_appendf(&fn_type->name, " %s", buf_ptr(&fn_type_id->return_type->name));
-
- // The fn_type is a pointer; not to be confused with the raw function type.
- fn_type->size_in_bits = g->builtin_types.entry_usize->size_in_bits;
- fn_type->abi_size = g->builtin_types.entry_usize->abi_size;
- fn_type->abi_align = g->builtin_types.entry_usize->abi_align;
-
- g->fn_type_table.put(&fn_type->data.fn.fn_type_id, fn_type);
-
- return fn_type;
-}
-
-static ZigTypeId container_to_type(ContainerKind kind) {
- switch (kind) {
- case ContainerKindStruct:
- return ZigTypeIdStruct;
- case ContainerKindEnum:
- return ZigTypeIdEnum;
- case ContainerKindUnion:
- return ZigTypeIdUnion;
- }
- zig_unreachable();
-}
-
-// This is like get_partial_container_type except it's for the implicit root struct of files.
-static ZigType *get_root_container_type(CodeGen *g, const char *full_name, Buf *bare_name,
- RootStruct *root_struct)
-{
- ZigType *entry = new_type_table_entry(ZigTypeIdStruct);
- entry->data.structure.decls_scope = create_decls_scope(g, nullptr, nullptr, entry, entry, bare_name);
- entry->data.structure.root_struct = root_struct;
- entry->data.structure.layout = ContainerLayoutAuto;
-
- if (full_name[0] == '\0') {
- buf_init_from_str(&entry->name, "(root)");
- } else {
- buf_init_from_str(&entry->name, full_name);
- }
-
- return entry;
-}
-
-ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind,
- AstNode *decl_node, const char *full_name, Buf *bare_name, ContainerLayout layout)
-{
- ZigTypeId type_id = container_to_type(kind);
- ZigType *entry = new_container_type_entry(g, type_id, decl_node, scope, bare_name);
-
- switch (kind) {
- case ContainerKindStruct:
- entry->data.structure.decl_node = decl_node;
- entry->data.structure.layout = layout;
- break;
- case ContainerKindEnum:
- entry->data.enumeration.decl_node = decl_node;
- entry->data.enumeration.layout = layout;
- break;
- case ContainerKindUnion:
- entry->data.unionation.decl_node = decl_node;
- entry->data.unionation.layout = layout;
- break;
- }
-
- buf_init_from_str(&entry->name, full_name);
-
- return entry;
-}
-
-ZigValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry,
- Buf *type_name, UndefAllowed undef)
-{
- Error err;
-
- ZigValue *result = g->pass1_arena->create();
- ZigValue *result_ptr = g->pass1_arena->create();
- result->special = ConstValSpecialUndef;
- result->type = (type_entry == nullptr) ? g->builtin_types.entry_anytype : type_entry;
- result_ptr->special = ConstValSpecialStatic;
- result_ptr->type = get_pointer_to_type(g, result->type, false);
- result_ptr->data.x_ptr.mut = ConstPtrMutComptimeVar;
- result_ptr->data.x_ptr.special = ConstPtrSpecialRef;
- result_ptr->data.x_ptr.data.ref.pointee = result;
-
- size_t backward_branch_count = 0;
- size_t backward_branch_quota = default_backward_branch_quota;
- if ((err = ir_eval_const_value(g, scope, node, result_ptr,
- &backward_branch_count, &backward_branch_quota,
- nullptr, nullptr, node, type_name, nullptr, nullptr, undef)))
- {
- return g->invalid_inst_gen->value;
- }
- return result;
-}
-
-Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent_type,
- ZigValue *parent_type_val, bool *is_zero_bits)
-{
- Error err;
- if (type_val->special != ConstValSpecialLazy) {
- assert(type_val->special == ConstValSpecialStatic);
-
- // Self-referencing types via pointers are allowed and have non-zero size
- ZigType *ty = type_val->data.x_type;
- while (ty->id == ZigTypeIdPointer &&
- !ty->data.pointer.resolve_loop_flag_zero_bits)
- {
- ty = ty->data.pointer.child_type;
- }
-
- if ((ty->id == ZigTypeIdStruct && ty->data.structure.resolve_loop_flag_zero_bits) ||
- (ty->id == ZigTypeIdUnion && ty->data.unionation.resolve_loop_flag_zero_bits) ||
- (ty->id == ZigTypeIdPointer && ty->data.pointer.resolve_loop_flag_zero_bits))
- {
- *is_zero_bits = false;
- return ErrorNone;
- }
-
- if ((err = type_resolve(g, type_val->data.x_type, ResolveStatusZeroBitsKnown)))
- return err;
-
- *is_zero_bits = (type_val->data.x_type->abi_size == 0);
- return ErrorNone;
- }
- switch (type_val->data.x_lazy->id) {
- case LazyValueIdInvalid:
- case LazyValueIdAlignOf:
- case LazyValueIdSizeOf:
- case LazyValueIdTypeInfoDecls:
- zig_unreachable();
- case LazyValueIdPtrType: {
- LazyValuePtrType *lazy_ptr_type = reinterpret_cast(type_val->data.x_lazy);
-
- if (parent_type_val == lazy_ptr_type->elem_type->value) {
- // Does a struct which contains a pointer field to itself have bits? Yes.
- *is_zero_bits = false;
- return ErrorNone;
- } else {
- if (parent_type_val == nullptr) {
- parent_type_val = type_val;
- }
- return type_val_resolve_zero_bits(g, lazy_ptr_type->elem_type->value, parent_type,
- parent_type_val, is_zero_bits);
- }
- }
- case LazyValueIdArrayType: {
- LazyValueArrayType *lazy_array_type =
- reinterpret_cast(type_val->data.x_lazy);
-
- // The sentinel counts as an extra element
- if (lazy_array_type->length == 0 && lazy_array_type->sentinel == nullptr) {
- *is_zero_bits = true;
- return ErrorNone;
- }
-
- if ((err = type_val_resolve_zero_bits(g, lazy_array_type->elem_type->value,
- parent_type, nullptr, is_zero_bits)))
- return err;
-
- return ErrorNone;
- }
- case LazyValueIdOptType:
- case LazyValueIdSliceType:
- case LazyValueIdErrUnionType:
- *is_zero_bits = false;
- return ErrorNone;
- case LazyValueIdFnType: {
- LazyValueFnType *lazy_fn_type = reinterpret_cast(type_val->data.x_lazy);
- *is_zero_bits = lazy_fn_type->is_generic;
- return ErrorNone;
- }
- }
- zig_unreachable();
-}
-
-Error type_val_resolve_is_opaque_type(CodeGen *g, ZigValue *type_val, bool *is_opaque_type) {
- if (type_val->special != ConstValSpecialLazy) {
- assert(type_val->special == ConstValSpecialStatic);
- if (type_val->data.x_type == g->builtin_types.entry_anytype) {
- *is_opaque_type = false;
- return ErrorNone;
- }
- *is_opaque_type = (type_val->data.x_type->id == ZigTypeIdOpaque);
- return ErrorNone;
- }
- switch (type_val->data.x_lazy->id) {
- case LazyValueIdInvalid:
- case LazyValueIdAlignOf:
- case LazyValueIdSizeOf:
- case LazyValueIdTypeInfoDecls:
- zig_unreachable();
- case LazyValueIdSliceType:
- case LazyValueIdPtrType:
- case LazyValueIdFnType:
- case LazyValueIdOptType:
- case LazyValueIdErrUnionType:
- case LazyValueIdArrayType:
- *is_opaque_type = false;
- return ErrorNone;
- }
- zig_unreachable();
-}
-
-static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ZigValue *type_val) {
- if (type_val->special != ConstValSpecialLazy) {
- return type_requires_comptime(g, type_val->data.x_type);
- }
- switch (type_val->data.x_lazy->id) {
- case LazyValueIdInvalid:
- case LazyValueIdAlignOf:
- case LazyValueIdSizeOf:
- case LazyValueIdTypeInfoDecls:
- zig_unreachable();
- case LazyValueIdSliceType: {
- LazyValueSliceType *lazy_slice_type = reinterpret_cast(type_val->data.x_lazy);
- return type_val_resolve_requires_comptime(g, lazy_slice_type->elem_type->value);
- }
- case LazyValueIdPtrType: {
- LazyValuePtrType *lazy_ptr_type = reinterpret_cast(type_val->data.x_lazy);
- return type_val_resolve_requires_comptime(g, lazy_ptr_type->elem_type->value);
- }
- case LazyValueIdOptType: {
- LazyValueOptType *lazy_opt_type = reinterpret_cast(type_val->data.x_lazy);
- return type_val_resolve_requires_comptime(g, lazy_opt_type->payload_type->value);
- }
- case LazyValueIdArrayType: {
- LazyValueArrayType *lazy_array_type = reinterpret_cast(type_val->data.x_lazy);
- return type_val_resolve_requires_comptime(g, lazy_array_type->elem_type->value);
- }
- case LazyValueIdFnType: {
- LazyValueFnType *lazy_fn_type = reinterpret_cast(type_val->data.x_lazy);
- if (lazy_fn_type->is_generic)
- return ReqCompTimeYes;
- switch (type_val_resolve_requires_comptime(g, lazy_fn_type->return_type->value)) {
- case ReqCompTimeInvalid:
- return ReqCompTimeInvalid;
- case ReqCompTimeYes:
- return ReqCompTimeYes;
- case ReqCompTimeNo:
- break;
- }
- size_t param_count = lazy_fn_type->proto_node->data.fn_proto.params.length;
- for (size_t i = 0; i < param_count; i += 1) {
- AstNode *param_node = lazy_fn_type->proto_node->data.fn_proto.params.at(i);
- bool param_is_var_args = param_node->data.param_decl.is_var_args;
- if (param_is_var_args) break;
- switch (type_val_resolve_requires_comptime(g, lazy_fn_type->param_types[i]->value)) {
- case ReqCompTimeInvalid:
- return ReqCompTimeInvalid;
- case ReqCompTimeYes:
- return ReqCompTimeYes;
- case ReqCompTimeNo:
- break;
- }
- }
- return ReqCompTimeNo;
- }
- case LazyValueIdErrUnionType: {
- LazyValueErrUnionType *lazy_err_union_type =
- reinterpret_cast(type_val->data.x_lazy);
- return type_val_resolve_requires_comptime(g, lazy_err_union_type->payload_type->value);
- }
- }
- zig_unreachable();
-}
-
-Error type_val_resolve_abi_size(CodeGen *g, AstNode *source_node, ZigValue *type_val,
- size_t *abi_size, size_t *size_in_bits)
-{
- Error err;
-
-start_over:
- if (type_val->special != ConstValSpecialLazy) {
- assert(type_val->special == ConstValSpecialStatic);
- ZigType *ty = type_val->data.x_type;
- if ((err = type_resolve(g, ty, ResolveStatusSizeKnown)))
- return err;
- *abi_size = ty->abi_size;
- *size_in_bits = ty->size_in_bits;
- return ErrorNone;
- }
- switch (type_val->data.x_lazy->id) {
- case LazyValueIdInvalid:
- case LazyValueIdAlignOf:
- case LazyValueIdSizeOf:
- case LazyValueIdTypeInfoDecls:
- zig_unreachable();
- case LazyValueIdSliceType: {
- LazyValueSliceType *lazy_slice_type = reinterpret_cast(type_val->data.x_lazy);
- bool is_zero_bits;
- if ((err = type_val_resolve_zero_bits(g, lazy_slice_type->elem_type->value, nullptr,
- nullptr, &is_zero_bits)))
- {
- return err;
- }
- if (is_zero_bits) {
- *abi_size = g->builtin_types.entry_usize->abi_size;
- *size_in_bits = g->builtin_types.entry_usize->size_in_bits;
- } else {
- *abi_size = g->builtin_types.entry_usize->abi_size * 2;
- *size_in_bits = g->builtin_types.entry_usize->size_in_bits * 2;
- }
- return ErrorNone;
- }
- case LazyValueIdPtrType: {
- LazyValuePtrType *lazy_ptr_type = reinterpret_cast(type_val->data.x_lazy);
- bool is_zero_bits;
- if ((err = type_val_resolve_zero_bits(g, lazy_ptr_type->elem_type->value, nullptr,
- nullptr, &is_zero_bits)))
- {
- return err;
- }
- if (is_zero_bits) {
- *abi_size = 0;
- *size_in_bits = 0;
- } else {
- *abi_size = g->builtin_types.entry_usize->abi_size;
- *size_in_bits = g->builtin_types.entry_usize->size_in_bits;
- }
- return ErrorNone;
- }
- case LazyValueIdFnType:
- *abi_size = g->builtin_types.entry_usize->abi_size;
- *size_in_bits = g->builtin_types.entry_usize->size_in_bits;
- return ErrorNone;
- case LazyValueIdOptType:
- case LazyValueIdErrUnionType:
- case LazyValueIdArrayType:
- if ((err = ir_resolve_lazy(g, source_node, type_val)))
- return err;
- goto start_over;
- }
- zig_unreachable();
-}
-
-Error type_val_resolve_abi_align(CodeGen *g, AstNode *source_node, ZigValue *type_val, uint32_t *abi_align) {
- Error err;
- if (type_val->special != ConstValSpecialLazy) {
- assert(type_val->special == ConstValSpecialStatic);
- ZigType *ty = type_val->data.x_type;
- if (ty->id == ZigTypeIdPointer) {
- *abi_align = g->builtin_types.entry_usize->abi_align;
- return ErrorNone;
- }
- if ((err = type_resolve(g, ty, ResolveStatusAlignmentKnown)))
- return err;
- *abi_align = ty->abi_align;
- return ErrorNone;
- }
- switch (type_val->data.x_lazy->id) {
- case LazyValueIdInvalid:
- case LazyValueIdAlignOf:
- case LazyValueIdSizeOf:
- case LazyValueIdTypeInfoDecls:
- zig_unreachable();
- case LazyValueIdSliceType:
- case LazyValueIdPtrType:
- case LazyValueIdFnType:
- *abi_align = g->builtin_types.entry_usize->abi_align;
- return ErrorNone;
- case LazyValueIdOptType: {
- if ((err = ir_resolve_lazy(g, nullptr, type_val)))
- return err;
-
- return type_val_resolve_abi_align(g, source_node, type_val, abi_align);
- }
- case LazyValueIdArrayType: {
- LazyValueArrayType *lazy_array_type =
- reinterpret_cast(type_val->data.x_lazy);
- return type_val_resolve_abi_align(g, source_node, lazy_array_type->elem_type->value, abi_align);
- }
- case LazyValueIdErrUnionType: {
- LazyValueErrUnionType *lazy_err_union_type =
- reinterpret_cast(type_val->data.x_lazy);
- uint32_t payload_abi_align;
- if ((err = type_val_resolve_abi_align(g, source_node, lazy_err_union_type->payload_type->value,
- &payload_abi_align)))
- {
- return err;
- }
- *abi_align = (payload_abi_align > g->err_tag_type->abi_align) ?
- payload_abi_align : g->err_tag_type->abi_align;
- return ErrorNone;
- }
- }
- zig_unreachable();
-}
-
-static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, ZigValue *type_val) {
- if (type_val->special != ConstValSpecialLazy) {
- return type_has_one_possible_value(g, type_val->data.x_type);
- }
- switch (type_val->data.x_lazy->id) {
- case LazyValueIdInvalid:
- case LazyValueIdAlignOf:
- case LazyValueIdSizeOf:
- case LazyValueIdTypeInfoDecls:
- zig_unreachable();
- case LazyValueIdSliceType: // it has the len field
- case LazyValueIdOptType: // it has the optional bit
- case LazyValueIdFnType:
- return OnePossibleValueNo;
- case LazyValueIdArrayType: {
- LazyValueArrayType *lazy_array_type =
- reinterpret_cast(type_val->data.x_lazy);
- if (lazy_array_type->length == 0)
- return OnePossibleValueYes;
- return type_val_resolve_has_one_possible_value(g, lazy_array_type->elem_type->value);
- }
- case LazyValueIdPtrType: {
- Error err;
- bool zero_bits;
- if ((err = type_val_resolve_zero_bits(g, type_val, nullptr, nullptr, &zero_bits))) {
- return OnePossibleValueInvalid;
- }
- if (zero_bits) {
- return OnePossibleValueYes;
- } else {
- return OnePossibleValueNo;
- }
- }
- case LazyValueIdErrUnionType: {
- LazyValueErrUnionType *lazy_err_union_type =
- reinterpret_cast(type_val->data.x_lazy);
- switch (type_val_resolve_has_one_possible_value(g, lazy_err_union_type->err_set_type->value)) {
- case OnePossibleValueInvalid:
- return OnePossibleValueInvalid;
- case OnePossibleValueNo:
- return OnePossibleValueNo;
- case OnePossibleValueYes:
- return type_val_resolve_has_one_possible_value(g, lazy_err_union_type->payload_type->value);
- }
- }
- }
- zig_unreachable();
-}
-
-ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) {
- Error err;
- // Hot path for simple identifiers, to avoid unnecessary memory allocations.
- if (node->type == NodeTypeSymbol) {
- Buf *variable_name = node->data.symbol_expr.symbol;
- if (buf_eql_str(variable_name, "_"))
- goto abort_hot_path;
- ZigType *primitive_type;
- if ((err = get_primitive_type(g, variable_name, &primitive_type))) {
- goto abort_hot_path;
- } else {
- return primitive_type;
- }
-abort_hot_path:;
- }
- ZigValue *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type,
- nullptr, UndefBad);
- if (type_is_invalid(result->type))
- return g->builtin_types.entry_invalid;
- src_assert(result->special == ConstValSpecialStatic, node);
- src_assert(result->data.x_type != nullptr, node);
- return result->data.x_type;
-}
-
-ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
- ZigType *fn_type = new_type_table_entry(ZigTypeIdFn);
- buf_resize(&fn_type->name, 0);
- buf_appendf(&fn_type->name, "fn(");
- size_t i = 0;
- for (; i < fn_type_id->next_param_index; i += 1) {
- const char *comma_str = (i == 0) ? "" : ",";
- buf_appendf(&fn_type->name, "%s%s", comma_str,
- buf_ptr(&fn_type_id->param_info[i].type->name));
- }
- for (; i < fn_type_id->param_count; i += 1) {
- const char *comma_str = (i == 0) ? "" : ",";
- buf_appendf(&fn_type->name, "%sanytype", comma_str);
- }
- buf_append_str(&fn_type->name, ")");
- if (fn_type_id->cc != CallingConventionUnspecified) {
- buf_appendf(&fn_type->name, " callconv(.%s)", calling_convention_name(fn_type_id->cc));
- }
- buf_append_str(&fn_type->name, " anytype");
-
- fn_type->data.fn.fn_type_id = *fn_type_id;
- fn_type->data.fn.is_generic = true;
- fn_type->abi_size = 0;
- fn_type->size_in_bits = 0;
- fn_type->abi_align = 0;
- return fn_type;
-}
-
-CallingConvention cc_from_fn_proto(AstNodeFnProto *fn_proto) {
- // Compatible with the C ABI
- if (fn_proto->is_extern || fn_proto->is_export)
- return CallingConventionC;
-
- return CallingConventionUnspecified;
-}
-
-void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, CallingConvention cc, size_t param_count_alloc) {
- assert(proto_node->type == NodeTypeFnProto);
- AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
-
- fn_type_id->cc = cc;
- fn_type_id->param_count = fn_proto->params.length;
- fn_type_id->param_info = heap::c_allocator.allocate(param_count_alloc);
- fn_type_id->next_param_index = 0;
- fn_type_id->is_var_args = fn_proto->is_var_args;
-}
-
-static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_t *result) {
- ZigValue *align_result = analyze_const_value(g, scope, node, get_align_amt_type(g),
- nullptr, UndefBad);
- if (type_is_invalid(align_result->type))
- return false;
-
- uint32_t align_bytes = bigint_as_u32(&align_result->data.x_bigint);
- if (align_bytes == 0) {
- add_node_error(g, node, buf_sprintf("alignment must be >= 1"));
- return false;
- }
- if (!is_power_of_2(align_bytes)) {
- add_node_error(g, node, buf_sprintf("alignment value %" PRIu32 " is not a power of 2", align_bytes));
- return false;
- }
-
- *result = align_bytes;
- return true;
-}
-
-static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf **out_buffer) {
- ZigType *ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
- PtrLenUnknown, 0, 0, 0, false);
- ZigType *str_type = get_slice_type(g, ptr_type);
- ZigValue *result_val = analyze_const_value(g, scope, node, str_type, nullptr, UndefBad);
- if (type_is_invalid(result_val->type))
- return false;
-
- ZigValue *ptr_field = result_val->data.x_struct.fields[slice_ptr_index];
- ZigValue *len_field = result_val->data.x_struct.fields[slice_len_index];
-
- assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray);
- ZigValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val;
- if (array_val->data.x_array.special == ConstArraySpecialBuf) {
- *out_buffer = array_val->data.x_array.data.s_buf;
- return true;
- }
- expand_undef_array(g, array_val);
- size_t len = bigint_as_usize(&len_field->data.x_bigint);
- Buf *result = buf_alloc();
- buf_resize(result, len);
- for (size_t i = 0; i < len; i += 1) {
- size_t new_index = ptr_field->data.x_ptr.data.base_array.elem_index + i;
- ZigValue *char_val = &array_val->data.x_array.data.s_none.elements[new_index];
- if (char_val->special == ConstValSpecialUndef) {
- add_node_error(g, node, buf_sprintf("use of undefined value"));
- return false;
- }
- uint64_t big_c = bigint_as_u64(&char_val->data.x_bigint);
- assert(big_c <= UINT8_MAX);
- uint8_t c = (uint8_t)big_c;
- buf_ptr(result)[i] = c;
- }
- *out_buffer = result;
- return true;
-}
-
-static Error emit_error_unless_type_allowed_in_packed_container(CodeGen *g, ZigType *type_entry,
- AstNode *source_node, const char* container_name)
-{
- Error err;
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdMetaType:
- case ZigTypeIdUnreachable:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdErrorUnion:
- case ZigTypeIdErrorSet:
- case ZigTypeIdBoundFn:
- case ZigTypeIdOpaque:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- add_node_error(g, source_node,
- buf_sprintf("type '%s' not allowed in packed %s; no guaranteed in-memory representation",
- buf_ptr(&type_entry->name), container_name));
- return ErrorSemanticAnalyzeFail;
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdInt:
- case ZigTypeIdFloat:
- case ZigTypeIdPointer:
- case ZigTypeIdFn:
- case ZigTypeIdVector:
- return ErrorNone;
- case ZigTypeIdArray: {
- ZigType *elem_type = type_entry->data.array.child_type;
- if ((err = emit_error_unless_type_allowed_in_packed_container(g, elem_type, source_node, container_name)))
- return err;
- // TODO revisit this when doing https://github.com/ziglang/zig/issues/1512
- if (type_size(g, type_entry) * 8 == type_size_bits(g, type_entry))
- return ErrorNone;
- add_node_error(g, source_node,
- buf_sprintf("array of '%s' not allowed in packed %s due to padding bits",
- buf_ptr(&elem_type->name), container_name));
- return ErrorSemanticAnalyzeFail;
- }
- case ZigTypeIdStruct:
- switch (type_entry->data.structure.layout) {
- case ContainerLayoutPacked:
- case ContainerLayoutExtern:
- return ErrorNone;
- case ContainerLayoutAuto:
- add_node_error(g, source_node,
- buf_sprintf("non-packed, non-extern struct '%s' not allowed in packed %s; no guaranteed in-memory representation",
- buf_ptr(&type_entry->name), container_name));
- return ErrorSemanticAnalyzeFail;
- }
- zig_unreachable();
- case ZigTypeIdUnion:
- switch (type_entry->data.unionation.layout) {
- case ContainerLayoutPacked:
- case ContainerLayoutExtern:
- return ErrorNone;
- case ContainerLayoutAuto:
- add_node_error(g, source_node,
- buf_sprintf("non-packed, non-extern union '%s' not allowed in packed %s; no guaranteed in-memory representation",
- buf_ptr(&type_entry->name), container_name));
- return ErrorSemanticAnalyzeFail;
- }
- zig_unreachable();
- case ZigTypeIdOptional: {
- ZigType *ptr_type;
- if ((err = get_codegen_ptr_type(g, type_entry, &ptr_type))) return err;
- if (ptr_type != nullptr) return ErrorNone;
-
- add_node_error(g, source_node,
- buf_sprintf("type '%s' not allowed in packed %s; no guaranteed in-memory representation",
- buf_ptr(&type_entry->name), container_name));
- return ErrorSemanticAnalyzeFail;
- }
- case ZigTypeIdEnum: {
- AstNode *decl_node = type_entry->data.enumeration.decl_node;
- if (decl_node->data.container_decl.init_arg_expr != nullptr) {
- return ErrorNone;
- }
- ErrorMsg *msg = add_node_error(g, source_node,
- buf_sprintf("type '%s' not allowed in packed %s; no guaranteed in-memory representation",
- buf_ptr(&type_entry->name), container_name));
- add_error_note(g, msg, decl_node,
- buf_sprintf("enum declaration does not specify an integer tag type"));
- return ErrorSemanticAnalyzeFail;
- }
- }
- zig_unreachable();
-}
-
-static Error emit_error_unless_type_allowed_in_packed_struct(CodeGen *g, ZigType *type_entry,
- AstNode *source_node)
-{
- return emit_error_unless_type_allowed_in_packed_container(g, type_entry, source_node, "struct");
-}
-
-static Error emit_error_unless_type_allowed_in_packed_union(CodeGen *g, ZigType *type_entry,
- AstNode *source_node)
-{
- return emit_error_unless_type_allowed_in_packed_container(g, type_entry, source_node, "union");
-}
-
-Error type_allowed_in_extern(CodeGen *g, ZigType *type_entry, bool *result) {
- Error err;
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdMetaType:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdErrorUnion:
- case ZigTypeIdErrorSet:
- case ZigTypeIdBoundFn:
- case ZigTypeIdVoid:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- *result = false;
- return ErrorNone;
- case ZigTypeIdOpaque:
- case ZigTypeIdUnreachable:
- case ZigTypeIdBool:
- *result = true;
- return ErrorNone;
- case ZigTypeIdInt:
- switch (type_entry->data.integral.bit_count) {
- case 8:
- case 16:
- case 32:
- case 64:
- case 128:
- *result = true;
- return ErrorNone;
- default:
- *result = false;
- return ErrorNone;
- }
- case ZigTypeIdVector:
- return type_allowed_in_extern(g, type_entry->data.vector.elem_type, result);
- case ZigTypeIdFloat:
- *result = true;
- return ErrorNone;
- case ZigTypeIdArray:
- return type_allowed_in_extern(g, type_entry->data.array.child_type, result);
- case ZigTypeIdFn:
- *result = !calling_convention_allows_zig_types(type_entry->data.fn.fn_type_id.cc);
- return ErrorNone;
- case ZigTypeIdPointer:
- if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown)))
- return err;
- if (!type_has_bits(g, type_entry)) {
- *result = false;
- return ErrorNone;
- }
- *result = true;
- return ErrorNone;
- case ZigTypeIdStruct:
- *result = type_entry->data.structure.layout == ContainerLayoutExtern ||
- type_entry->data.structure.layout == ContainerLayoutPacked;
- return ErrorNone;
- case ZigTypeIdOptional: {
- ZigType *child_type = type_entry->data.maybe.child_type;
- if (child_type->id != ZigTypeIdPointer && child_type->id != ZigTypeIdFn) {
- *result = false;
- return ErrorNone;
- }
- if (!type_is_nonnull_ptr(g, child_type)) {
- *result = false;
- return ErrorNone;
- }
- return type_allowed_in_extern(g, child_type, result);
- }
- case ZigTypeIdEnum:
- *result = type_entry->data.enumeration.layout == ContainerLayoutExtern ||
- type_entry->data.enumeration.layout == ContainerLayoutPacked;
- return ErrorNone;
- case ZigTypeIdUnion:
- *result = type_entry->data.unionation.layout == ContainerLayoutExtern ||
- type_entry->data.unionation.layout == ContainerLayoutPacked;
- return ErrorNone;
- }
- zig_unreachable();
-}
-
-ZigType *get_auto_err_set_type(CodeGen *g, ZigFn *fn_entry) {
- ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
- buf_resize(&err_set_type->name, 0);
- buf_appendf(&err_set_type->name, "@typeInfo(@typeInfo(@TypeOf(%s)).Fn.return_type.?).ErrorUnion.error_set", buf_ptr(&fn_entry->symbol_name));
- err_set_type->data.error_set.err_count = 0;
- err_set_type->data.error_set.errors = nullptr;
- err_set_type->data.error_set.infer_fn = fn_entry;
- err_set_type->data.error_set.incomplete = true;
- err_set_type->size_in_bits = g->builtin_types.entry_global_error_set->size_in_bits;
- err_set_type->abi_align = g->builtin_types.entry_global_error_set->abi_align;
- err_set_type->abi_size = g->builtin_types.entry_global_error_set->abi_size;
-
- return err_set_type;
-}
-
-static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope, ZigFn *fn_entry,
- CallingConvention cc)
-{
- assert(proto_node->type == NodeTypeFnProto);
- AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
- Error err;
-
- FnTypeId fn_type_id = {0};
- init_fn_type_id(&fn_type_id, proto_node, cc, proto_node->data.fn_proto.params.length);
-
- for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) {
- AstNode *param_node = fn_proto->params.at(fn_type_id.next_param_index);
- assert(param_node->type == NodeTypeParamDecl);
-
- bool param_is_comptime = param_node->data.param_decl.is_comptime;
- bool param_is_var_args = param_node->data.param_decl.is_var_args;
-
- if (param_is_comptime) {
- if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
- add_node_error(g, param_node,
- buf_sprintf("comptime parameter not allowed in function with calling convention '%s'",
- calling_convention_name(fn_type_id.cc)));
- return g->builtin_types.entry_invalid;
- }
- if (param_node->data.param_decl.type != nullptr) {
- ZigType *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type);
- if (type_is_invalid(type_entry)) {
- return g->builtin_types.entry_invalid;
- }
- FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];
- param_info->type = type_entry;
- param_info->is_noalias = param_node->data.param_decl.is_noalias;
- fn_type_id.next_param_index += 1;
- }
-
- return get_generic_fn_type(g, &fn_type_id);
- } else if (param_is_var_args) {
- if (fn_type_id.cc == CallingConventionC) {
- fn_type_id.param_count = fn_type_id.next_param_index;
- continue;
- } else {
- add_node_error(g, param_node,
- buf_sprintf("var args only allowed in functions with C calling convention"));
- return g->builtin_types.entry_invalid;
- }
- } else if (param_node->data.param_decl.anytype_token != nullptr) {
- if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
- add_node_error(g, param_node,
- buf_sprintf("parameter of type 'anytype' not allowed in function with calling convention '%s'",
- calling_convention_name(fn_type_id.cc)));
- return g->builtin_types.entry_invalid;
- }
- return get_generic_fn_type(g, &fn_type_id);
- }
-
- ZigType *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type);
- if (type_is_invalid(type_entry)) {
- return g->builtin_types.entry_invalid;
- }
- if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
- if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown)))
- return g->builtin_types.entry_invalid;
- if (!type_has_bits(g, type_entry)) {
- add_node_error(g, param_node->data.param_decl.type,
- buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'",
- buf_ptr(&type_entry->name), calling_convention_name(fn_type_id.cc)));
- return g->builtin_types.entry_invalid;
- }
- }
-
- if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
- bool ok_type;
- if ((err = type_allowed_in_extern(g, type_entry, &ok_type)))
- return g->builtin_types.entry_invalid;
- if (!ok_type) {
- add_node_error(g, param_node->data.param_decl.type,
- buf_sprintf("parameter of type '%s' not allowed in function with calling convention '%s'",
- buf_ptr(&type_entry->name),
- calling_convention_name(fn_type_id.cc)));
- return g->builtin_types.entry_invalid;
- }
- }
-
- if(!is_valid_param_type(type_entry)){
- if(type_entry->id == ZigTypeIdOpaque){
- add_node_error(g, param_node->data.param_decl.type,
- buf_sprintf("parameter of opaque type '%s' not allowed", buf_ptr(&type_entry->name)));
- } else {
- add_node_error(g, param_node->data.param_decl.type,
- buf_sprintf("parameter of type '%s' not allowed", buf_ptr(&type_entry->name)));
- }
-
- return g->builtin_types.entry_invalid;
- }
-
- switch (type_requires_comptime(g, type_entry)) {
- case ReqCompTimeNo:
- break;
- case ReqCompTimeYes:
- add_node_error(g, param_node->data.param_decl.type,
- buf_sprintf("parameter of type '%s' must be declared comptime",
- buf_ptr(&type_entry->name)));
- return g->builtin_types.entry_invalid;
- case ReqCompTimeInvalid:
- return g->builtin_types.entry_invalid;
- }
-
- FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];
- param_info->type = type_entry;
- param_info->is_noalias = param_node->data.param_decl.is_noalias;
- }
-
- if (fn_proto->align_expr != nullptr) {
- if (target_is_wasm(g->zig_target)) {
- // In Wasm, specifying alignment of function pointers makes little sense
- // since function pointers are in fact indices to a Wasm table, therefore
- // any alignment check on those is invalid. This can cause unexpected
- // behaviour when checking expected alignment with `@ptrToInt(fn_ptr)`
- // or similar. This commit proposes to make `align` expressions a
- // compile error when compiled to Wasm architecture.
- //
- // Some references:
- // [1] [Mozilla: WebAssembly Tables](https://developer.mozilla.org/en-US/docs/WebAssembly/Understanding_the_text_format#WebAssembly_tables)
- // [2] [Sunfishcode's Wasm Ref Manual](https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#indirect-call)
- add_node_error(g, fn_proto->align_expr,
- buf_sprintf("align(N) expr is not allowed on function prototypes in wasm32/wasm64"));
- return g->builtin_types.entry_invalid;
- }
- if (!analyze_const_align(g, child_scope, fn_proto->align_expr, &fn_type_id.alignment)) {
- return g->builtin_types.entry_invalid;
- }
- fn_entry->align_bytes = fn_type_id.alignment;
- }
-
- if (fn_proto->return_anytype_token != nullptr) {
- if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
- add_node_error(g, fn_proto->return_type,
- buf_sprintf("return type 'anytype' not allowed in function with calling convention '%s'",
- calling_convention_name(fn_type_id.cc)));
- return g->builtin_types.entry_invalid;
- }
- add_node_error(g, proto_node,
- buf_sprintf("TODO implement inferred return types https://github.com/ziglang/zig/issues/447"));
- return g->builtin_types.entry_invalid;
- }
-
- ZigType *specified_return_type = analyze_type_expr(g, child_scope, fn_proto->return_type);
- if (type_is_invalid(specified_return_type)) {
- fn_type_id.return_type = g->builtin_types.entry_invalid;
- return g->builtin_types.entry_invalid;
- }
-
- if(!is_valid_return_type(specified_return_type)){
- ErrorMsg* msg = add_node_error(g, fn_proto->return_type,
- buf_sprintf("%s return type '%s' not allowed", type_id_name(specified_return_type->id), buf_ptr(&specified_return_type->name)));
- Tld *tld = find_decl(g, &fn_entry->fndef_scope->base, &specified_return_type->name);
- if (tld != nullptr) {
- add_error_note(g, msg, tld->source_node, buf_sprintf("type declared here"));
- }
- return g->builtin_types.entry_invalid;
- }
-
- if (fn_proto->auto_err_set) {
- ZigType *inferred_err_set_type = get_auto_err_set_type(g, fn_entry);
- if ((err = type_resolve(g, specified_return_type, ResolveStatusSizeKnown)))
- return g->builtin_types.entry_invalid;
- fn_type_id.return_type = get_error_union_type(g, inferred_err_set_type, specified_return_type);
- } else {
- fn_type_id.return_type = specified_return_type;
- }
-
- if (!calling_convention_allows_zig_types(fn_type_id.cc) &&
- fn_type_id.return_type->id != ZigTypeIdVoid)
- {
- if ((err = type_resolve(g, fn_type_id.return_type, ResolveStatusSizeKnown)))
- return g->builtin_types.entry_invalid;
- bool ok_type;
- if ((err = type_allowed_in_extern(g, fn_type_id.return_type, &ok_type)))
- return g->builtin_types.entry_invalid;
- if (!ok_type) {
- add_node_error(g, fn_proto->return_type,
- buf_sprintf("return type '%s' not allowed in function with calling convention '%s'",
- buf_ptr(&fn_type_id.return_type->name),
- calling_convention_name(fn_type_id.cc)));
- return g->builtin_types.entry_invalid;
- }
- }
-
- switch (type_requires_comptime(g, fn_type_id.return_type)) {
- case ReqCompTimeInvalid:
- return g->builtin_types.entry_invalid;
- case ReqCompTimeYes:
- return get_generic_fn_type(g, &fn_type_id);
- case ReqCompTimeNo:
- break;
- }
-
- return get_fn_type(g, &fn_type_id);
-}
-
-bool is_valid_return_type(ZigType* type) {
- switch (type->id) {
- case ZigTypeIdInvalid:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdOpaque:
- return false;
- default:
- return true;
- }
- zig_unreachable();
-}
-
-bool is_valid_param_type(ZigType* type) {
- switch (type->id) {
- case ZigTypeIdInvalid:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdOpaque:
- case ZigTypeIdUnreachable:
- return false;
- default:
- return true;
- }
- zig_unreachable();
-}
-
-bool type_is_invalid(ZigType *type_entry) {
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- return true;
- case ZigTypeIdStruct:
- return type_entry->data.structure.resolve_status == ResolveStatusInvalid;
- case ZigTypeIdUnion:
- return type_entry->data.unionation.resolve_status == ResolveStatusInvalid;
- case ZigTypeIdEnum:
- return type_entry->data.enumeration.resolve_status == ResolveStatusInvalid;
- case ZigTypeIdFnFrame:
- return type_entry->data.frame.reported_loop_err;
- default:
- return false;
- }
- zig_unreachable();
-}
-
-struct SrcField {
- const char *name;
- ZigType *ty;
- unsigned align;
-};
-
-static ZigType *get_struct_type(CodeGen *g, const char *type_name, SrcField fields[], size_t field_count,
- unsigned min_abi_align)
-{
- ZigType *struct_type = new_type_table_entry(ZigTypeIdStruct);
-
- buf_init_from_str(&struct_type->name, type_name);
-
- struct_type->data.structure.src_field_count = field_count;
- struct_type->data.structure.gen_field_count = 0;
- struct_type->data.structure.resolve_status = ResolveStatusSizeKnown;
- struct_type->data.structure.fields = alloc_type_struct_fields(field_count);
- struct_type->data.structure.fields_by_name.init(field_count);
-
- size_t abi_align = min_abi_align;
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *field = struct_type->data.structure.fields[i];
- field->name = buf_create_from_str(fields[i].name);
- field->type_entry = fields[i].ty;
- field->src_index = i;
- field->align = fields[i].align;
-
- if (type_has_bits(g, field->type_entry)) {
- assert(type_is_resolved(field->type_entry, ResolveStatusSizeKnown));
- unsigned field_abi_align = max(field->align, field->type_entry->abi_align);
- if (field_abi_align > abi_align) {
- abi_align = field_abi_align;
- }
- }
-
- auto prev_entry = struct_type->data.structure.fields_by_name.put_unique(field->name, field);
- assert(prev_entry == nullptr);
- }
-
- size_t next_offset = 0;
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *field = struct_type->data.structure.fields[i];
- if (!type_has_bits(g, field->type_entry))
- continue;
-
- field->offset = next_offset;
-
- // find the next non-zero-byte field for offset calculations
- size_t next_src_field_index = i + 1;
- for (; next_src_field_index < field_count; next_src_field_index += 1) {
- if (type_has_bits(g, struct_type->data.structure.fields[next_src_field_index]->type_entry))
- break;
- }
- size_t next_abi_align;
- if (next_src_field_index == field_count) {
- next_abi_align = abi_align;
- } else {
- next_abi_align = max(fields[next_src_field_index].align,
- struct_type->data.structure.fields[next_src_field_index]->type_entry->abi_align);
- }
- next_offset = next_field_offset(next_offset, abi_align, field->type_entry->abi_size, next_abi_align);
- }
-
- struct_type->abi_align = abi_align;
- struct_type->abi_size = next_offset;
- struct_type->size_in_bits = next_offset * 8;
-
- return struct_type;
-}
-
-static size_t get_store_size_bytes(size_t size_in_bits) {
- return (size_in_bits + 7) / 8;
-}
-
-static size_t get_abi_align_bytes(size_t size_in_bits, size_t pointer_size_bytes) {
- size_t store_size_bytes = get_store_size_bytes(size_in_bits);
- if (store_size_bytes >= pointer_size_bytes)
- return pointer_size_bytes;
- return round_to_next_power_of_2(store_size_bytes);
-}
-
-static size_t get_abi_size_bytes(size_t size_in_bits, size_t pointer_size_bytes) {
- size_t store_size_bytes = get_store_size_bytes(size_in_bits);
- size_t abi_align = get_abi_align_bytes(size_in_bits, pointer_size_bytes);
- return align_forward(store_size_bytes, abi_align);
-}
-
-ZigType *resolve_struct_field_type(CodeGen *g, TypeStructField *struct_field) {
- Error err;
- if (struct_field->type_entry == nullptr) {
- if ((err = ir_resolve_lazy(g, struct_field->decl_node, struct_field->type_val))) {
- return nullptr;
- }
- struct_field->type_entry = struct_field->type_val->data.x_type;
- }
- return struct_field->type_entry;
-}
-
-static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
- assert(struct_type->id == ZigTypeIdStruct);
-
- Error err;
-
- if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
- if (struct_type->data.structure.resolve_status >= ResolveStatusSizeKnown)
- return ErrorNone;
-
- if ((err = resolve_struct_alignment(g, struct_type)))
- return err;
-
- AstNode *decl_node = struct_type->data.structure.decl_node;
-
- if (struct_type->data.structure.resolve_loop_flag_other) {
- if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- add_node_error(g, decl_node,
- buf_sprintf("struct '%s' depends on itself", buf_ptr(&struct_type->name)));
- }
- return ErrorSemanticAnalyzeFail;
- }
-
- assert(struct_type->data.structure.fields || struct_type->data.structure.src_field_count == 0);
-
- size_t field_count = struct_type->data.structure.src_field_count;
-
- bool packed = (struct_type->data.structure.layout == ContainerLayoutPacked);
- struct_type->data.structure.resolve_loop_flag_other = true;
-
- uint32_t *host_int_bytes = packed ? heap::c_allocator.allocate(struct_type->data.structure.gen_field_count) : nullptr;
-
- size_t packed_bits_offset = 0;
- size_t next_offset = 0;
- size_t first_packed_bits_offset_misalign = SIZE_MAX;
- size_t gen_field_index = 0;
- size_t size_in_bits = 0;
- size_t abi_align = struct_type->abi_align;
-
- // Calculate offsets
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *field = struct_type->data.structure.fields[i];
- if (field->gen_index == SIZE_MAX)
- continue;
-
- field->gen_index = gen_field_index;
- field->offset = next_offset;
-
- if (packed) {
- ZigType *field_type = resolve_struct_field_type(g, field);
- if (field_type == nullptr) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- if ((err = type_resolve(g, field->type_entry, ResolveStatusSizeKnown))) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return err;
- }
- if ((err = emit_error_unless_type_allowed_in_packed_struct(g, field->type_entry, field->decl_node))) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return err;
- }
-
- size_t field_size_in_bits = type_size_bits(g, field_type);
- size_t next_packed_bits_offset = packed_bits_offset + field_size_in_bits;
-
- size_in_bits += field_size_in_bits;
-
- if (first_packed_bits_offset_misalign != SIZE_MAX) {
- // this field is not byte-aligned; it is part of the previous field with a bit offset
- field->bit_offset_in_host = packed_bits_offset - first_packed_bits_offset_misalign;
-
- size_t full_bit_count = next_packed_bits_offset - first_packed_bits_offset_misalign;
- size_t full_abi_size = get_abi_size_bytes(full_bit_count, g->pointer_size_bytes);
- if (full_abi_size * 8 == full_bit_count) {
- // next field recovers ABI alignment
- host_int_bytes[gen_field_index] = full_abi_size;
- gen_field_index += 1;
- // TODO: https://github.com/ziglang/zig/issues/1512
- next_offset = next_field_offset(next_offset, abi_align, full_abi_size, 1);
- size_in_bits = next_offset * 8;
-
- first_packed_bits_offset_misalign = SIZE_MAX;
- }
- } else if (get_abi_size_bytes(field_type->size_in_bits, g->pointer_size_bytes) * 8 != field_size_in_bits) {
- first_packed_bits_offset_misalign = packed_bits_offset;
- field->bit_offset_in_host = 0;
- } else {
- // This is a byte-aligned field (both start and end) in a packed struct.
- host_int_bytes[gen_field_index] = field_type->size_in_bits / 8;
- field->bit_offset_in_host = 0;
- gen_field_index += 1;
- // TODO: https://github.com/ziglang/zig/issues/1512
- next_offset = next_field_offset(next_offset, abi_align, field_type->size_in_bits / 8, 1);
- size_in_bits = next_offset * 8;
- }
- packed_bits_offset = next_packed_bits_offset;
- } else {
- size_t field_abi_size;
- size_t field_size_in_bits;
- if ((err = type_val_resolve_abi_size(g, field->decl_node, field->type_val,
- &field_abi_size, &field_size_in_bits)))
- {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return err;
- }
-
- gen_field_index += 1;
- size_t next_src_field_index = i + 1;
- for (; next_src_field_index < field_count; next_src_field_index += 1) {
- if (struct_type->data.structure.fields[next_src_field_index]->gen_index != SIZE_MAX) {
- break;
- }
- }
- size_t next_align = (next_src_field_index == field_count) ?
- abi_align : struct_type->data.structure.fields[next_src_field_index]->align;
- next_offset = next_field_offset(next_offset, abi_align, field_abi_size, next_align);
- size_in_bits = next_offset * 8;
- }
- }
- if (first_packed_bits_offset_misalign != SIZE_MAX) {
- size_t full_bit_count = packed_bits_offset - first_packed_bits_offset_misalign;
- size_t full_abi_size = get_abi_size_bytes(full_bit_count, g->pointer_size_bytes);
- next_offset = next_field_offset(next_offset, abi_align, full_abi_size, abi_align);
- host_int_bytes[gen_field_index] = full_abi_size;
- gen_field_index += 1;
- }
-
- struct_type->abi_size = next_offset;
- struct_type->size_in_bits = size_in_bits;
- struct_type->data.structure.resolve_status = ResolveStatusSizeKnown;
- struct_type->data.structure.gen_field_count = (uint32_t)gen_field_index;
- struct_type->data.structure.resolve_loop_flag_other = false;
- struct_type->data.structure.host_int_bytes = host_int_bytes;
-
-
- // Resolve types for fields
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *field = struct_type->data.structure.fields[i];
- ZigType *field_type = resolve_struct_field_type(g, field);
- if (field_type == nullptr) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
-
- if ((err = type_resolve(g, field_type, ResolveStatusSizeKnown))) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return err;
- }
-
- if (struct_type->data.structure.layout == ContainerLayoutExtern) {
- bool ok_type;
- if ((err = type_allowed_in_extern(g, field_type, &ok_type))) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- if (!ok_type) {
- add_node_error(g, field->decl_node,
- buf_sprintf("extern structs cannot contain fields of type '%s'",
- buf_ptr(&field_type->name)));
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- }
- }
-
- return ErrorNone;
-}
-
-static Error resolve_union_alignment(CodeGen *g, ZigType *union_type) {
- assert(union_type->id == ZigTypeIdUnion);
-
- Error err;
-
- if (union_type->data.unionation.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
- if (union_type->data.unionation.resolve_status >= ResolveStatusAlignmentKnown)
- return ErrorNone;
- if ((err = resolve_union_zero_bits(g, union_type)))
- return err;
- if (union_type->data.unionation.resolve_status >= ResolveStatusAlignmentKnown)
- return ErrorNone;
-
- AstNode *decl_node = union_type->data.structure.decl_node;
-
- if (union_type->data.unionation.resolve_loop_flag_other) {
- if (union_type->data.unionation.resolve_status != ResolveStatusInvalid) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- add_node_error(g, decl_node,
- buf_sprintf("union '%s' depends on itself", buf_ptr(&union_type->name)));
- }
- return ErrorSemanticAnalyzeFail;
- }
-
- // set temporary flag
- union_type->data.unionation.resolve_loop_flag_other = true;
-
- TypeUnionField *most_aligned_union_member = nullptr;
- uint32_t field_count = union_type->data.unionation.src_field_count;
- bool packed = union_type->data.unionation.layout == ContainerLayoutPacked;
-
- for (uint32_t i = 0; i < field_count; i += 1) {
- TypeUnionField *field = &union_type->data.unionation.fields[i];
- if (field->gen_index == UINT32_MAX)
- continue;
-
- AstNode *align_expr = nullptr;
- if (union_type->data.unionation.decl_node->type == NodeTypeContainerDecl) {
- align_expr = field->decl_node->data.struct_field.align_expr;
- }
- if (align_expr != nullptr) {
- if (!analyze_const_align(g, &union_type->data.unionation.decls_scope->base, align_expr,
- &field->align))
- {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- add_node_error(g, field->decl_node,
- buf_create_from_str("TODO implement field alignment syntax for unions. https://github.com/ziglang/zig/issues/3125"));
- } else if (packed) {
- field->align = 1;
- } else if (field->type_entry != nullptr) {
- if ((err = type_resolve(g, field->type_entry, ResolveStatusAlignmentKnown))) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return err;
- }
- field->align = field->type_entry->abi_align;
- } else {
- if ((err = type_val_resolve_abi_align(g, field->decl_node, field->type_val, &field->align))) {
- if (g->trace_err != nullptr) {
- g->trace_err = add_error_note(g, g->trace_err, field->decl_node,
- buf_create_from_str("while checking this field"));
- }
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return err;
- }
- if (union_type->data.unionation.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
- }
-
- if (most_aligned_union_member == nullptr || field->align > most_aligned_union_member->align) {
- most_aligned_union_member = field;
- }
- }
-
- // unset temporary flag
- union_type->data.unionation.resolve_loop_flag_other = false;
- union_type->data.unionation.resolve_status = ResolveStatusAlignmentKnown;
- union_type->data.unionation.most_aligned_union_member = most_aligned_union_member;
-
- ZigType *tag_type = union_type->data.unionation.tag_type;
- if (tag_type != nullptr && type_has_bits(g, tag_type)) {
- if ((err = type_resolve(g, tag_type, ResolveStatusAlignmentKnown))) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- if (most_aligned_union_member == nullptr) {
- union_type->abi_align = tag_type->abi_align;
- union_type->data.unionation.gen_tag_index = SIZE_MAX;
- union_type->data.unionation.gen_union_index = SIZE_MAX;
- } else if (tag_type->abi_align > most_aligned_union_member->align) {
- union_type->abi_align = tag_type->abi_align;
- union_type->data.unionation.gen_tag_index = 0;
- union_type->data.unionation.gen_union_index = 1;
- } else {
- union_type->abi_align = most_aligned_union_member->align;
- union_type->data.unionation.gen_union_index = 0;
- union_type->data.unionation.gen_tag_index = 1;
- }
- } else {
- assert(most_aligned_union_member != nullptr);
- union_type->abi_align = most_aligned_union_member->align;
- union_type->data.unionation.gen_union_index = SIZE_MAX;
- union_type->data.unionation.gen_tag_index = SIZE_MAX;
- }
-
- return ErrorNone;
-}
-
-ZigType *resolve_union_field_type(CodeGen *g, TypeUnionField *union_field) {
- Error err;
- if (union_field->type_entry == nullptr) {
- if ((err = ir_resolve_lazy(g, union_field->decl_node, union_field->type_val))) {
- return nullptr;
- }
- union_field->type_entry = union_field->type_val->data.x_type;
- }
- return union_field->type_entry;
-}
-
-static Error resolve_union_type(CodeGen *g, ZigType *union_type) {
- assert(union_type->id == ZigTypeIdUnion);
-
- Error err;
-
- if (union_type->data.unionation.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
- if (union_type->data.unionation.resolve_status >= ResolveStatusSizeKnown)
- return ErrorNone;
-
- if ((err = resolve_union_alignment(g, union_type)))
- return err;
-
- AstNode *decl_node = union_type->data.unionation.decl_node;
-
- uint32_t field_count = union_type->data.unionation.src_field_count;
- TypeUnionField *most_aligned_union_member = union_type->data.unionation.most_aligned_union_member;
-
- assert(union_type->data.unionation.fields);
-
- size_t union_abi_size = 0;
- size_t union_size_in_bits = 0;
-
- if (union_type->data.unionation.resolve_loop_flag_other) {
- if (union_type->data.unionation.resolve_status != ResolveStatusInvalid) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- add_node_error(g, decl_node,
- buf_sprintf("union '%s' depends on itself", buf_ptr(&union_type->name)));
- }
- return ErrorSemanticAnalyzeFail;
- }
-
- // set temporary flag
- union_type->data.unionation.resolve_loop_flag_other = true;
-
- const bool is_packed = union_type->data.unionation.layout == ContainerLayoutPacked;
-
- for (uint32_t i = 0; i < field_count; i += 1) {
- TypeUnionField *union_field = &union_type->data.unionation.fields[i];
- ZigType *field_type = resolve_union_field_type(g, union_field);
- if (field_type == nullptr) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
-
- if ((err = type_resolve(g, field_type, ResolveStatusSizeKnown))) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
-
- if (is_packed) {
- if ((err = emit_error_unless_type_allowed_in_packed_union(g, field_type, union_field->decl_node))) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return err;
- }
- }
-
- if (type_is_invalid(union_type))
- return ErrorSemanticAnalyzeFail;
-
- if (!type_has_bits(g, field_type))
- continue;
-
- union_abi_size = max(union_abi_size, field_type->abi_size);
- union_size_in_bits = max(union_size_in_bits, field_type->size_in_bits);
- }
-
- // The union itself for now has to be treated as being independently aligned.
- // See https://github.com/ziglang/zig/issues/2166.
- if (most_aligned_union_member != nullptr) {
- union_abi_size = align_forward(union_abi_size, most_aligned_union_member->align);
- }
-
- // unset temporary flag
- union_type->data.unionation.resolve_loop_flag_other = false;
- union_type->data.unionation.resolve_status = ResolveStatusSizeKnown;
- union_type->data.unionation.union_abi_size = union_abi_size;
-
- ZigType *tag_type = union_type->data.unionation.tag_type;
- if (tag_type != nullptr && type_has_bits(g, tag_type)) {
- if ((err = type_resolve(g, tag_type, ResolveStatusSizeKnown))) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- if (most_aligned_union_member == nullptr) {
- union_type->abi_size = tag_type->abi_size;
- union_type->size_in_bits = tag_type->size_in_bits;
- } else {
- size_t field_sizes[2];
- size_t field_aligns[2];
- field_sizes[union_type->data.unionation.gen_tag_index] = tag_type->abi_size;
- field_aligns[union_type->data.unionation.gen_tag_index] = tag_type->abi_align;
- field_sizes[union_type->data.unionation.gen_union_index] = union_abi_size;
- field_aligns[union_type->data.unionation.gen_union_index] = most_aligned_union_member->align;
- size_t field2_offset = next_field_offset(0, union_type->abi_align, field_sizes[0], field_aligns[1]);
- union_type->abi_size = next_field_offset(field2_offset, union_type->abi_align, field_sizes[1], union_type->abi_align);
- union_type->size_in_bits = union_type->abi_size * 8;
- }
- } else {
- union_type->abi_size = union_abi_size;
- union_type->size_in_bits = union_size_in_bits;
- }
-
- return ErrorNone;
-}
-
-static Error type_is_valid_extern_enum_tag(CodeGen *g, ZigType *ty, bool *result) {
- // Only integer types are allowed by the C ABI
- if(ty->id != ZigTypeIdInt) {
- *result = false;
- return ErrorNone;
- }
-
- // According to the ANSI C standard the enumeration type should be either a
- // signed char, a signed integer or an unsigned one. But GCC/Clang allow
- // other integral types as a compiler extension so let's accomodate them
- // aswell.
- return type_allowed_in_extern(g, ty, result);
-}
-
-static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
- Error err;
- assert(enum_type->id == ZigTypeIdEnum);
-
- if (enum_type->data.enumeration.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
- if (enum_type->data.enumeration.resolve_status >= ResolveStatusZeroBitsKnown)
- return ErrorNone;
-
- AstNode *decl_node = enum_type->data.enumeration.decl_node;
-
- if (enum_type->data.enumeration.resolve_loop_flag) {
- if (enum_type->data.enumeration.resolve_status != ResolveStatusInvalid) {
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- add_node_error(g, decl_node,
- buf_sprintf("enum '%s' depends on itself",
- buf_ptr(&enum_type->name)));
- }
- return ErrorSemanticAnalyzeFail;
- }
-
- enum_type->data.enumeration.resolve_loop_flag = true;
-
- uint32_t field_count;
- if (decl_node->type == NodeTypeContainerDecl) {
- assert(!enum_type->data.enumeration.fields);
- field_count = (uint32_t)decl_node->data.container_decl.fields.length;
- } else {
- field_count = enum_type->data.enumeration.src_field_count + enum_type->data.enumeration.non_exhaustive;
- }
-
- if (field_count == 0) {
- add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields"));
- enum_type->data.enumeration.src_field_count = field_count;
- enum_type->data.enumeration.fields = nullptr;
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
-
- Scope *scope = &enum_type->data.enumeration.decls_scope->base;
-
- ZigType *tag_int_type;
- if (enum_type->data.enumeration.layout == ContainerLayoutExtern) {
- tag_int_type = get_c_int_type(g, CIntTypeInt);
- } else {
- tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1);
- }
-
- enum_type->size_in_bits = tag_int_type->size_in_bits;
- enum_type->abi_size = tag_int_type->abi_size;
- enum_type->abi_align = tag_int_type->abi_align;
-
- ZigType *wanted_tag_int_type = nullptr;
- if (decl_node->type == NodeTypeContainerDecl) {
- if (decl_node->data.container_decl.init_arg_expr != nullptr) {
- wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr);
- }
- } else {
- wanted_tag_int_type = enum_type->data.enumeration.tag_int_type;
- }
-
- if (wanted_tag_int_type != nullptr) {
- if (type_is_invalid(wanted_tag_int_type)) {
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- } else if (wanted_tag_int_type->id != ZigTypeIdInt &&
- wanted_tag_int_type->id != ZigTypeIdComptimeInt) {
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- add_node_error(g, decl_node->data.container_decl.init_arg_expr,
- buf_sprintf("expected integer, found '%s'", buf_ptr(&wanted_tag_int_type->name)));
- } else {
- if (enum_type->data.enumeration.layout == ContainerLayoutExtern) {
- bool ok_type;
- if ((err = type_is_valid_extern_enum_tag(g, wanted_tag_int_type, &ok_type))) {
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- return err;
- }
- if (!ok_type) {
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- ErrorMsg *msg = add_node_error(g, decl_node->data.container_decl.init_arg_expr,
- buf_sprintf("'%s' is not a valid tag type for an extern enum",
- buf_ptr(&wanted_tag_int_type->name)));
- add_error_note(g, msg, decl_node->data.container_decl.init_arg_expr,
- buf_sprintf("any integral type of size 8, 16, 32, 64 or 128 bit is valid"));
- return ErrorSemanticAnalyzeFail;
- }
- }
- tag_int_type = wanted_tag_int_type;
- }
- }
-
- enum_type->data.enumeration.tag_int_type = tag_int_type;
- enum_type->size_in_bits = tag_int_type->size_in_bits;
- enum_type->abi_size = tag_int_type->abi_size;
- enum_type->abi_align = tag_int_type->abi_align;
-
- BigInt bi_one;
- bigint_init_unsigned(&bi_one, 1);
-
- if (decl_node->type == NodeTypeContainerDecl) {
- AstNode *last_field_node = decl_node->data.container_decl.fields.at(field_count - 1);
- if (buf_eql_str(last_field_node->data.struct_field.name, "_")) {
- if (last_field_node->data.struct_field.value != nullptr) {
- add_node_error(g, last_field_node, buf_sprintf("value assigned to '_' field of non-exhaustive enum"));
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- }
- if (decl_node->data.container_decl.init_arg_expr == nullptr) {
- add_node_error(g, decl_node, buf_sprintf("non-exhaustive enum must specify size"));
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- }
- enum_type->data.enumeration.non_exhaustive = true;
- } else {
- enum_type->data.enumeration.non_exhaustive = false;
- }
- }
-
- if (enum_type->data.enumeration.non_exhaustive) {
- field_count -= 1;
- if (field_count > 1 && log2_u64(field_count) == enum_type->size_in_bits) {
- add_node_error(g, decl_node, buf_sprintf("non-exhaustive enum specifies every value"));
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- }
- }
-
- if (decl_node->type == NodeTypeContainerDecl) {
- enum_type->data.enumeration.src_field_count = field_count;
- enum_type->data.enumeration.fields = heap::c_allocator.allocate(field_count);
- enum_type->data.enumeration.fields_by_name.init(field_count);
-
- HashMap occupied_tag_values = {};
- occupied_tag_values.init(field_count);
-
- TypeEnumField *last_enum_field = nullptr;
-
- for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {
- AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);
- TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];
- type_enum_field->name = field_node->data.struct_field.name;
- type_enum_field->decl_index = field_i;
- type_enum_field->decl_node = field_node;
-
- if (field_node->data.struct_field.type != nullptr) {
- ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.type,
- buf_sprintf("structs and unions, not enums, support field types"));
- add_error_note(g, msg, decl_node,
- buf_sprintf("consider 'union(enum)' here"));
- } else if (field_node->data.struct_field.align_expr != nullptr) {
- ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.align_expr,
- buf_sprintf("structs and unions, not enums, support field alignment"));
- add_error_note(g, msg, decl_node,
- buf_sprintf("consider 'union(enum)' here"));
- }
-
- if (buf_eql_str(type_enum_field->name, "_")) {
- add_node_error(g, field_node, buf_sprintf("'_' field of non-exhaustive enum must be last"));
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- }
-
- auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field);
- if (field_entry != nullptr) {
- ErrorMsg *msg = add_node_error(g, field_node,
- buf_sprintf("duplicate enum field: '%s'", buf_ptr(type_enum_field->name)));
- add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here"));
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- continue;
- }
-
- AstNode *tag_value = field_node->data.struct_field.value;
-
- if (tag_value != nullptr) {
- // A user-specified value is available
- ZigValue *result = analyze_const_value(g, scope, tag_value, tag_int_type,
- nullptr, UndefBad);
- if (type_is_invalid(result->type)) {
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- continue;
- }
-
- assert(result->special != ConstValSpecialRuntime);
- assert(result->type->id == ZigTypeIdInt || result->type->id == ZigTypeIdComptimeInt);
-
- bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint);
- } else {
- // No value was explicitly specified: allocate the last value + 1
- // or, if this is the first element, zero
- if (last_enum_field != nullptr) {
- bigint_add(&type_enum_field->value, &last_enum_field->value, &bi_one);
- } else {
- bigint_init_unsigned(&type_enum_field->value, 0);
- }
-
- // Make sure we can represent this number with tag_int_type
- if (!bigint_fits_in_bits(&type_enum_field->value,
- tag_int_type->size_in_bits,
- tag_int_type->data.integral.is_signed)) {
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
-
- Buf *val_buf = buf_alloc();
- bigint_append_buf(val_buf, &type_enum_field->value, 10);
- add_node_error(g, field_node,
- buf_sprintf("enumeration value %s too large for type '%s'",
- buf_ptr(val_buf), buf_ptr(&tag_int_type->name)));
-
- break;
- }
- }
-
- // Make sure the value is unique
- auto entry = occupied_tag_values.put_unique(type_enum_field->value, field_node);
- if (entry != nullptr && enum_type->data.enumeration.layout != ContainerLayoutExtern) {
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
-
- Buf *val_buf = buf_alloc();
- bigint_append_buf(val_buf, &type_enum_field->value, 10);
-
- ErrorMsg *msg = add_node_error(g, field_node,
- buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf)));
- add_error_note(g, msg, entry->value,
- buf_sprintf("other occurrence here"));
- }
-
- last_enum_field = type_enum_field;
- }
- occupied_tag_values.deinit();
- }
-
- if (enum_type->data.enumeration.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
-
- enum_type->data.enumeration.resolve_loop_flag = false;
- enum_type->data.enumeration.resolve_status = ResolveStatusSizeKnown;
-
- return ErrorNone;
-}
-
-static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) {
- assert(struct_type->id == ZigTypeIdStruct);
-
- Error err;
-
- if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
- if (struct_type->data.structure.resolve_status >= ResolveStatusZeroBitsKnown)
- return ErrorNone;
-
- AstNode *decl_node = struct_type->data.structure.decl_node;
-
- if (struct_type->data.structure.resolve_loop_flag_zero_bits) {
- if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- add_node_error(g, decl_node,
- buf_sprintf("struct '%s' depends on itself",
- buf_ptr(&struct_type->name)));
- }
- return ErrorSemanticAnalyzeFail;
- }
- struct_type->data.structure.resolve_loop_flag_zero_bits = true;
-
- size_t field_count;
- if (decl_node->type == NodeTypeContainerDecl) {
- field_count = decl_node->data.container_decl.fields.length;
- struct_type->data.structure.src_field_count = (uint32_t)field_count;
-
- src_assert(struct_type->data.structure.fields == nullptr, decl_node);
- struct_type->data.structure.fields = alloc_type_struct_fields(field_count);
- } else if (is_anon_container(struct_type) || struct_type->data.structure.created_by_at_type) {
- field_count = struct_type->data.structure.src_field_count;
-
- src_assert(field_count == 0 || struct_type->data.structure.fields != nullptr, decl_node);
- } else zig_unreachable();
-
- struct_type->data.structure.fields_by_name.init(field_count);
-
- Scope *scope = &struct_type->data.structure.decls_scope->base;
-
- size_t gen_field_index = 0;
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *type_struct_field = struct_type->data.structure.fields[i];
-
- AstNode *field_node;
- if (decl_node->type == NodeTypeContainerDecl) {
- field_node = decl_node->data.container_decl.fields.at(i);
- type_struct_field->name = field_node->data.struct_field.name;
- type_struct_field->decl_node = field_node;
- if (field_node->data.struct_field.comptime_token != nullptr) {
- if (field_node->data.struct_field.value == nullptr) {
- add_token_error(g, field_node->owner,
- field_node->data.struct_field.comptime_token,
- buf_sprintf("comptime struct field missing initialization value"));
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- type_struct_field->is_comptime = true;
- }
-
- if (field_node->data.struct_field.type == nullptr) {
- add_node_error(g, field_node, buf_sprintf("struct field missing type"));
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- } else if (is_anon_container(struct_type) || struct_type->data.structure.created_by_at_type) {
- field_node = type_struct_field->decl_node;
-
- src_assert(type_struct_field->type_entry != nullptr, field_node);
- } else zig_unreachable();
-
- auto field_entry = struct_type->data.structure.fields_by_name.put_unique(type_struct_field->name, type_struct_field);
- if (field_entry != nullptr) {
- ErrorMsg *msg = add_node_error(g, field_node,
- buf_sprintf("duplicate struct field: '%s'", buf_ptr(type_struct_field->name)));
- add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here"));
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
-
- ZigValue *field_type_val;
- if (decl_node->type == NodeTypeContainerDecl) {
- field_type_val = analyze_const_value(g, scope,
- field_node->data.struct_field.type, g->builtin_types.entry_type, nullptr, LazyOkNoUndef);
- if (type_is_invalid(field_type_val->type)) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- assert(field_type_val->special != ConstValSpecialRuntime);
- type_struct_field->type_val = field_type_val;
- if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
- } else if (is_anon_container(struct_type) || struct_type->data.structure.created_by_at_type) {
- field_type_val = type_struct_field->type_val;
- } else zig_unreachable();
-
- bool field_is_opaque_type;
- if ((err = type_val_resolve_is_opaque_type(g, field_type_val, &field_is_opaque_type))) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- if (field_is_opaque_type) {
- add_node_error(g, field_node,
- buf_sprintf("opaque types have unknown size and therefore cannot be directly embedded in structs"));
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
-
- type_struct_field->src_index = i;
- type_struct_field->gen_index = SIZE_MAX;
-
- if (type_struct_field->is_comptime)
- continue;
-
- switch (type_val_resolve_requires_comptime(g, field_type_val)) {
- case ReqCompTimeYes:
- struct_type->data.structure.requires_comptime = true;
- break;
- case ReqCompTimeInvalid:
- if (g->trace_err != nullptr) {
- g->trace_err = add_error_note(g, g->trace_err, field_node,
- buf_create_from_str("while checking this field"));
- }
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- case ReqCompTimeNo:
- break;
- }
-
- bool field_is_zero_bits;
- if ((err = type_val_resolve_zero_bits(g, field_type_val, struct_type, nullptr, &field_is_zero_bits))) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- if (field_is_zero_bits)
- continue;
-
- type_struct_field->gen_index = gen_field_index;
- gen_field_index += 1;
- }
-
- struct_type->data.structure.resolve_loop_flag_zero_bits = false;
- struct_type->data.structure.gen_field_count = (uint32_t)gen_field_index;
- if (gen_field_index != 0) {
- struct_type->abi_size = SIZE_MAX;
- struct_type->size_in_bits = SIZE_MAX;
- }
-
- if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
-
- struct_type->data.structure.resolve_status = ResolveStatusZeroBitsKnown;
- return ErrorNone;
-}
-
-static Error resolve_struct_alignment(CodeGen *g, ZigType *struct_type) {
- assert(struct_type->id == ZigTypeIdStruct);
-
- Error err;
-
- if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
- if (struct_type->data.structure.resolve_status >= ResolveStatusAlignmentKnown)
- return ErrorNone;
- if ((err = resolve_struct_zero_bits(g, struct_type)))
- return err;
- if (struct_type->data.structure.resolve_status >= ResolveStatusAlignmentKnown)
- return ErrorNone;
-
- AstNode *decl_node = struct_type->data.structure.decl_node;
-
- if (struct_type->data.structure.resolve_loop_flag_other) {
- if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- add_node_error(g, decl_node,
- buf_sprintf("struct '%s' depends on itself", buf_ptr(&struct_type->name)));
- }
- return ErrorSemanticAnalyzeFail;
- }
-
- struct_type->data.structure.resolve_loop_flag_other = true;
-
- size_t field_count = struct_type->data.structure.src_field_count;
- bool packed = struct_type->data.structure.layout == ContainerLayoutPacked;
-
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *field = struct_type->data.structure.fields[i];
- if (field->gen_index == SIZE_MAX)
- continue;
-
- AstNode *align_expr = (field->decl_node->type == NodeTypeStructField) ?
- field->decl_node->data.struct_field.align_expr : nullptr;
- if (align_expr != nullptr) {
- if (!analyze_const_align(g, &struct_type->data.structure.decls_scope->base, align_expr,
- &field->align))
- {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- } else if (packed) {
- field->align = 1;
- } else {
- if ((err = type_val_resolve_abi_align(g, field->decl_node, field->type_val, &field->align))) {
- if (g->trace_err != nullptr) {
- g->trace_err = add_error_note(g, g->trace_err, field->decl_node,
- buf_create_from_str("while checking this field"));
- }
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return err;
- }
- if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
- }
-
- if (field->align > struct_type->abi_align) {
- struct_type->abi_align = field->align;
- }
- }
-
- if (!type_has_bits(g, struct_type)) {
- assert(struct_type->abi_align == 0);
- }
-
- struct_type->data.structure.resolve_loop_flag_other = false;
-
- if (struct_type->data.structure.resolve_status == ResolveStatusInvalid) {
- return ErrorSemanticAnalyzeFail;
- }
-
- struct_type->data.structure.resolve_status = ResolveStatusAlignmentKnown;
- return ErrorNone;
-}
-
-static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
- assert(union_type->id == ZigTypeIdUnion);
-
- Error err;
-
- if (union_type->data.unionation.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
-
- if (union_type->data.unionation.resolve_status >= ResolveStatusZeroBitsKnown)
- return ErrorNone;
-
- AstNode *decl_node = union_type->data.unionation.decl_node;
-
- if (union_type->data.unionation.resolve_loop_flag_zero_bits) {
- if (union_type->data.unionation.resolve_status != ResolveStatusInvalid) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- add_node_error(g, decl_node,
- buf_sprintf("union '%s' depends on itself",
- buf_ptr(&union_type->name)));
- }
- return ErrorSemanticAnalyzeFail;
- }
-
- union_type->data.unionation.resolve_loop_flag_zero_bits = true;
-
- uint32_t field_count;
- if (decl_node->type == NodeTypeContainerDecl) {
- assert(union_type->data.unionation.fields == nullptr);
- field_count = (uint32_t)decl_node->data.container_decl.fields.length;
- union_type->data.unionation.src_field_count = field_count;
- union_type->data.unionation.fields = heap::c_allocator.allocate(field_count);
- union_type->data.unionation.fields_by_name.init(field_count);
- } else {
- field_count = union_type->data.unionation.src_field_count;
- assert(field_count == 0 || union_type->data.unionation.fields != nullptr);
- }
-
- if (field_count == 0) {
- add_node_error(g, decl_node, buf_sprintf("unions must have 1 or more fields"));
- union_type->data.unionation.src_field_count = field_count;
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
-
- Scope *scope = &union_type->data.unionation.decls_scope->base;
-
- HashMap occupied_tag_values = {};
-
- bool is_auto_enum; // union(enum) or union(enum(expr))
- bool is_explicit_enum; // union(expr)
- AstNode *enum_type_node; // expr in union(enum(expr)) or union(expr)
- if (decl_node->type == NodeTypeContainerDecl) {
- is_auto_enum = decl_node->data.container_decl.auto_enum;
- is_explicit_enum = decl_node->data.container_decl.init_arg_expr != nullptr;
- enum_type_node = decl_node->data.container_decl.init_arg_expr;
- } else {
- is_auto_enum = false;
- is_explicit_enum = union_type->data.unionation.tag_type != nullptr;
- enum_type_node = nullptr;
- }
- union_type->data.unionation.have_explicit_tag_type = is_auto_enum || is_explicit_enum;
-
- bool is_auto_layout = union_type->data.unionation.layout == ContainerLayoutAuto;
- bool want_safety = (field_count >= 2)
- && (is_auto_layout || is_explicit_enum)
- && !(g->build_mode == BuildModeFastRelease || g->build_mode == BuildModeSmallRelease);
- ZigType *tag_type;
- bool create_enum_type = is_auto_enum || (!is_explicit_enum && want_safety);
- bool *covered_enum_fields;
- bool *is_zero_bits = heap::c_allocator.allocate(field_count);
- ZigLLVMDIEnumerator **di_enumerators;
- if (create_enum_type) {
- occupied_tag_values.init(field_count);
-
- di_enumerators = heap::c_allocator.allocate(field_count);
-
- ZigType *tag_int_type;
- if (enum_type_node != nullptr) {
- tag_int_type = analyze_type_expr(g, scope, enum_type_node);
- if (type_is_invalid(tag_int_type)) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- if (tag_int_type->id != ZigTypeIdInt && tag_int_type->id != ZigTypeIdComptimeInt) {
- add_node_error(g, enum_type_node,
- buf_sprintf("expected integer tag type, found '%s'", buf_ptr(&tag_int_type->name)));
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- } else {
- tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1);
- }
-
- tag_type = new_type_table_entry(ZigTypeIdEnum);
- buf_resize(&tag_type->name, 0);
- buf_appendf(&tag_type->name, "@TagType(%s)", buf_ptr(&union_type->name));
- tag_type->llvm_type = tag_int_type->llvm_type;
- tag_type->llvm_di_type = tag_int_type->llvm_di_type;
- tag_type->abi_size = tag_int_type->abi_size;
- tag_type->abi_align = tag_int_type->abi_align;
- tag_type->size_in_bits = tag_int_type->size_in_bits;
-
- tag_type->data.enumeration.tag_int_type = tag_int_type;
- tag_type->data.enumeration.resolve_status = ResolveStatusSizeKnown;
- tag_type->data.enumeration.decl_node = decl_node;
- tag_type->data.enumeration.layout = ContainerLayoutAuto;
- tag_type->data.enumeration.src_field_count = field_count;
- tag_type->data.enumeration.fields = heap::c_allocator.allocate(field_count);
- tag_type->data.enumeration.fields_by_name.init(field_count);
- tag_type->data.enumeration.decls_scope = union_type->data.unionation.decls_scope;
- } else if (enum_type_node != nullptr) {
- tag_type = analyze_type_expr(g, scope, enum_type_node);
- } else {
- if (decl_node->type == NodeTypeContainerDecl) {
- tag_type = nullptr;
- } else {
- tag_type = union_type->data.unionation.tag_type;
- }
- }
- if (tag_type != nullptr) {
- if (type_is_invalid(tag_type)) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- if (tag_type->id != ZigTypeIdEnum) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- add_node_error(g, enum_type_node != nullptr ? enum_type_node : decl_node,
- buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&tag_type->name)));
- return ErrorSemanticAnalyzeFail;
- }
- if ((err = type_resolve(g, tag_type, ResolveStatusAlignmentKnown))) {
- assert(g->errors.length != 0);
- return err;
- }
- covered_enum_fields = heap::c_allocator.allocate(tag_type->data.enumeration.src_field_count);
- }
- union_type->data.unionation.tag_type = tag_type;
-
- for (uint32_t i = 0; i < field_count; i += 1) {
- TypeUnionField *union_field = &union_type->data.unionation.fields[i];
- if (decl_node->type == NodeTypeContainerDecl) {
- AstNode *field_node = decl_node->data.container_decl.fields.at(i);
- union_field->name = field_node->data.struct_field.name;
- union_field->decl_node = field_node;
- union_field->gen_index = UINT32_MAX;
- is_zero_bits[i] = false;
-
- auto field_entry = union_type->data.unionation.fields_by_name.put_unique(union_field->name, union_field);
- if (field_entry != nullptr) {
- ErrorMsg *msg = add_node_error(g, union_field->decl_node,
- buf_sprintf("duplicate union field: '%s'", buf_ptr(union_field->name)));
- add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here"));
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
-
- if (field_node->data.struct_field.type == nullptr) {
- if (is_auto_enum || is_explicit_enum) {
- union_field->type_entry = g->builtin_types.entry_void;
- is_zero_bits[i] = true;
- } else {
- add_node_error(g, field_node, buf_sprintf("union field missing type"));
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- } else {
- ZigValue *field_type_val = analyze_const_value(g, scope,
- field_node->data.struct_field.type, g->builtin_types.entry_type, nullptr, LazyOkNoUndef);
- if (type_is_invalid(field_type_val->type)) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- assert(field_type_val->special != ConstValSpecialRuntime);
- union_field->type_val = field_type_val;
- }
-
- if (field_node->data.struct_field.value != nullptr && !is_auto_enum) {
- ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value,
- buf_create_from_str("untagged union field assignment"));
- add_error_note(g, msg, decl_node, buf_create_from_str("consider 'union(enum)' here"));
- }
- }
-
- if (union_field->type_val != nullptr) {
- bool field_is_opaque_type;
- if ((err = type_val_resolve_is_opaque_type(g, union_field->type_val, &field_is_opaque_type))) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- if (field_is_opaque_type) {
- add_node_error(g, union_field->decl_node,
- buf_create_from_str(
- "opaque types have unknown size and therefore cannot be directly embedded in unions"));
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
-
- switch (type_val_resolve_requires_comptime(g, union_field->type_val)) {
- case ReqCompTimeInvalid:
- if (g->trace_err != nullptr) {
- g->trace_err = add_error_note(g, g->trace_err, union_field->decl_node,
- buf_create_from_str("while checking this field"));
- }
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- case ReqCompTimeYes:
- union_type->data.unionation.requires_comptime = true;
- break;
- case ReqCompTimeNo:
- break;
- }
-
- if ((err = type_val_resolve_zero_bits(g, union_field->type_val, union_type, nullptr, &is_zero_bits[i]))) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- }
-
- if (create_enum_type) {
- di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(union_field->name), i);
- union_field->enum_field = &tag_type->data.enumeration.fields[i];
- union_field->enum_field->name = union_field->name;
- union_field->enum_field->decl_index = i;
- union_field->enum_field->decl_node = union_field->decl_node;
-
- auto prev_entry = tag_type->data.enumeration.fields_by_name.put_unique(union_field->enum_field->name, union_field->enum_field);
- assert(prev_entry == nullptr); // caught by union de-duplicator above
-
- AstNode *tag_value = decl_node->type == NodeTypeContainerDecl
- ? union_field->decl_node->data.struct_field.value : nullptr;
-
- // In this first pass we resolve explicit tag values.
- // In a second pass we will fill in the unspecified ones.
- if (tag_value != nullptr) {
- ZigType *tag_int_type = tag_type->data.enumeration.tag_int_type;
- ZigValue *result = analyze_const_value(g, scope, tag_value, tag_int_type,
- nullptr, UndefBad);
- if (type_is_invalid(result->type)) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- assert(result->special != ConstValSpecialRuntime);
- assert(result->type->id == ZigTypeIdInt);
- auto entry = occupied_tag_values.put_unique(result->data.x_bigint, tag_value);
- if (entry == nullptr) {
- bigint_init_bigint(&union_field->enum_field->value, &result->data.x_bigint);
- } else {
- Buf *val_buf = buf_alloc();
- bigint_append_buf(val_buf, &result->data.x_bigint, 10);
-
- ErrorMsg *msg = add_node_error(g, tag_value,
- buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf)));
- add_error_note(g, msg, entry->value,
- buf_sprintf("other occurrence here"));
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- }
- } else if (tag_type != nullptr) {
- union_field->enum_field = find_enum_type_field(tag_type, union_field->name);
- if (union_field->enum_field == nullptr) {
- ErrorMsg *msg = add_node_error(g, union_field->decl_node,
- buf_sprintf("enum field not found: '%s'", buf_ptr(union_field->name)));
- add_error_note(g, msg, tag_type->data.enumeration.decl_node,
- buf_sprintf("enum declared here"));
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- covered_enum_fields[union_field->enum_field->decl_index] = true;
- } else {
- union_field->enum_field = heap::c_allocator.create();
- union_field->enum_field->name = union_field->name;
- union_field->enum_field->decl_index = i;
- bigint_init_unsigned(&union_field->enum_field->value, i);
- }
- assert(union_field->enum_field != nullptr);
- }
-
- uint32_t gen_field_index = 0;
- for (uint32_t i = 0; i < field_count; i += 1) {
- TypeUnionField *union_field = &union_type->data.unionation.fields[i];
- if (!is_zero_bits[i]) {
- union_field->gen_index = gen_field_index;
- gen_field_index += 1;
- }
- }
-
- bool src_have_tag = is_auto_enum || is_explicit_enum;
-
- if (src_have_tag && union_type->data.unionation.layout != ContainerLayoutAuto) {
- const char *qual_str;
- switch (union_type->data.unionation.layout) {
- case ContainerLayoutAuto:
- zig_unreachable();
- case ContainerLayoutPacked:
- qual_str = "packed";
- break;
- case ContainerLayoutExtern:
- qual_str = "extern";
- break;
- }
- AstNode *source_node = enum_type_node != nullptr ? enum_type_node : decl_node;
- add_node_error(g, source_node,
- buf_sprintf("%s union does not support enum tag type", qual_str));
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
-
- if (create_enum_type) {
- if (decl_node->type == NodeTypeContainerDecl) {
- // Now iterate again and populate the unspecified tag values
- uint32_t next_maybe_unoccupied_index = 0;
-
- for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {
- AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);
- TypeUnionField *union_field = &union_type->data.unionation.fields[field_i];
- AstNode *tag_value = field_node->data.struct_field.value;
-
- if (tag_value == nullptr) {
- if (occupied_tag_values.size() == 0) {
- bigint_init_unsigned(&union_field->enum_field->value, next_maybe_unoccupied_index);
- next_maybe_unoccupied_index += 1;
- } else {
- BigInt proposed_value;
- for (;;) {
- bigint_init_unsigned(&proposed_value, next_maybe_unoccupied_index);
- next_maybe_unoccupied_index += 1;
- auto entry = occupied_tag_values.put_unique(proposed_value, field_node);
- if (entry != nullptr) {
- continue;
- }
- break;
- }
- bigint_init_bigint(&union_field->enum_field->value, &proposed_value);
- }
- }
- }
- }
- } else if (tag_type != nullptr) {
- for (uint32_t i = 0; i < tag_type->data.enumeration.src_field_count; i += 1) {
- TypeEnumField *enum_field = &tag_type->data.enumeration.fields[i];
- if (!covered_enum_fields[i]) {
- ErrorMsg *msg = add_node_error(g, decl_node,
- buf_sprintf("enum field missing: '%s'", buf_ptr(enum_field->name)));
- if (decl_node->type == NodeTypeContainerDecl) {
- AstNode *enum_decl_node = tag_type->data.enumeration.decl_node;
- AstNode *field_node = enum_decl_node->data.container_decl.fields.at(i);
- add_error_note(g, msg, field_node,
- buf_sprintf("declared here"));
- }
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- }
- }
- }
-
- if (union_type->data.unionation.resolve_status == ResolveStatusInvalid) {
- return ErrorSemanticAnalyzeFail;
- }
-
- union_type->data.unionation.resolve_loop_flag_zero_bits = false;
-
- union_type->data.unionation.gen_field_count = gen_field_index;
- bool zero_bits = gen_field_index == 0 && (field_count < 2 || !src_have_tag);
- if (!zero_bits) {
- union_type->abi_size = SIZE_MAX;
- union_type->size_in_bits = SIZE_MAX;
- }
- union_type->data.unionation.resolve_status = zero_bits ? ResolveStatusSizeKnown : ResolveStatusZeroBitsKnown;
-
- return ErrorNone;
-}
-
-void append_namespace_qualification(CodeGen *g, Buf *buf, ZigType *container_type) {
- if (g->root_import == container_type || buf_len(&container_type->name) == 0) return;
- buf_append_buf(buf, &container_type->name);
- buf_append_char(buf, NAMESPACE_SEP_CHAR);
-}
-
-static void get_fully_qualified_decl_name(CodeGen *g, Buf *buf, Tld *tld, bool is_test) {
- buf_resize(buf, 0);
-
- Scope *scope = tld->parent_scope;
- while (scope->id != ScopeIdDecls) {
- scope = scope->parent;
- }
- ScopeDecls *decls_scope = reinterpret_cast(scope);
- append_namespace_qualification(g, buf, decls_scope->container_type);
- if (is_test) {
- buf_append_str(buf, "test \"");
- buf_append_buf(buf, tld->name);
- buf_append_char(buf, '"');
- } else {
- buf_append_buf(buf, tld->name);
- }
-}
-
-static ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value) {
- ZigFn *fn_entry = heap::c_allocator.create();
- fn_entry->ir_executable = heap::c_allocator.create();
-
- fn_entry->prealloc_backward_branch_quota = default_backward_branch_quota;
-
- fn_entry->analyzed_executable.backward_branch_count = &fn_entry->prealloc_bbc;
- fn_entry->analyzed_executable.backward_branch_quota = &fn_entry->prealloc_backward_branch_quota;
- fn_entry->analyzed_executable.fn_entry = fn_entry;
- fn_entry->ir_executable->fn_entry = fn_entry;
- fn_entry->fn_inline = inline_value;
-
- return fn_entry;
-}
-
-ZigFn *create_fn(CodeGen *g, AstNode *proto_node) {
- assert(proto_node->type == NodeTypeFnProto);
- AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
-
- ZigFn *fn_entry = create_fn_raw(g, fn_proto->fn_inline);
-
- fn_entry->proto_node = proto_node;
- fn_entry->body_node = (proto_node->data.fn_proto.fn_def_node == nullptr) ? nullptr :
- proto_node->data.fn_proto.fn_def_node->data.fn_def.body;
-
- fn_entry->analyzed_executable.source_node = fn_entry->body_node;
-
- return fn_entry;
-}
-
-ZigType *get_test_fn_type(CodeGen *g) {
- if (g->test_fn_type)
- return g->test_fn_type;
-
- FnTypeId fn_type_id = {0};
- fn_type_id.return_type = get_error_union_type(g, g->builtin_types.entry_global_error_set,
- g->builtin_types.entry_void);
- g->test_fn_type = get_fn_type(g, &fn_type_id);
- return g->test_fn_type;
-}
-
-void add_var_export(CodeGen *g, ZigVar *var, const char *symbol_name, GlobalLinkageId linkage) {
- GlobalExport *global_export = var->export_list.add_one();
- memset(global_export, 0, sizeof(GlobalExport));
- buf_init_from_str(&global_export->name, symbol_name);
- global_export->linkage = linkage;
-}
-
-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;
- } else if (cc == CallingConventionStdcall && g->zig_target->os == OsWindows) {
- if (strcmp(symbol_name, "WinMain") == 0) {
- g->have_winmain = true;
- } else if (strcmp(symbol_name, "wWinMain") == 0) {
- g->have_wwinmain = true;
- } else if (strcmp(symbol_name, "WinMainCRTStartup") == 0) {
- g->have_winmain_crt_startup = true;
- } else if (strcmp(symbol_name, "wWinMainCRTStartup") == 0) {
- g->have_wwinmain_crt_startup = true;
- } else if (strcmp(symbol_name, "DllMainCRTStartup") == 0) {
- g->have_dllmain_crt_startup = true;
- }
- }
-
- GlobalExport *fn_export = fn_table_entry->export_list.add_one();
- memset(fn_export, 0, sizeof(GlobalExport));
- buf_init_from_str(&fn_export->name, symbol_name);
- fn_export->linkage = linkage;
-}
-
-static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
- AstNode *source_node = tld_fn->base.source_node;
- if (source_node->type == NodeTypeFnProto) {
- AstNodeFnProto *fn_proto = &source_node->data.fn_proto;
-
- AstNode *fn_def_node = fn_proto->fn_def_node;
-
- ZigFn *fn_table_entry = create_fn(g, source_node);
- tld_fn->fn_entry = fn_table_entry;
-
- bool is_extern = (fn_table_entry->body_node == nullptr);
- if (fn_proto->is_export || is_extern) {
- buf_init_from_buf(&fn_table_entry->symbol_name, tld_fn->base.name);
- } else {
- get_fully_qualified_decl_name(g, &fn_table_entry->symbol_name, &tld_fn->base, false);
- }
-
- if (!is_extern) {
- fn_table_entry->fndef_scope = create_fndef_scope(g,
- fn_table_entry->body_node, tld_fn->base.parent_scope, fn_table_entry);
-
- for (size_t i = 0; i < fn_proto->params.length; i += 1) {
- AstNode *param_node = fn_proto->params.at(i);
- assert(param_node->type == NodeTypeParamDecl);
- if (param_node->data.param_decl.name == nullptr) {
- add_node_error(g, param_node, buf_sprintf("missing parameter name"));
- }
- }
- } else {
- fn_table_entry->inferred_async_node = inferred_async_none;
- g->external_symbol_names.put_unique(tld_fn->base.name, &tld_fn->base);
- }
-
- Scope *child_scope = fn_table_entry->fndef_scope ? &fn_table_entry->fndef_scope->base : tld_fn->base.parent_scope;
-
- CallingConvention cc;
- if (fn_proto->callconv_expr != nullptr) {
- ZigType *cc_enum_value = get_builtin_type(g, "CallingConvention");
-
- ZigValue *result_val = analyze_const_value(g, child_scope, fn_proto->callconv_expr,
- cc_enum_value, nullptr, UndefBad);
- if (type_is_invalid(result_val->type)) {
- fn_table_entry->type_entry = g->builtin_types.entry_invalid;
- tld_fn->base.resolution = TldResolutionInvalid;
- return;
- }
-
- cc = (CallingConvention)bigint_as_u32(&result_val->data.x_enum_tag);
- } else {
- cc = cc_from_fn_proto(fn_proto);
- }
-
- if (fn_proto->section_expr != nullptr) {
- if (!analyze_const_string(g, child_scope, fn_proto->section_expr, &fn_table_entry->section_name)) {
- fn_table_entry->type_entry = g->builtin_types.entry_invalid;
- tld_fn->base.resolution = TldResolutionInvalid;
- return;
- }
- }
-
- fn_table_entry->type_entry = analyze_fn_type(g, source_node, child_scope, fn_table_entry, cc);
-
- if (type_is_invalid(fn_table_entry->type_entry)) {
- tld_fn->base.resolution = TldResolutionInvalid;
- return;
- }
-
- const CallingConvention fn_cc = fn_table_entry->type_entry->data.fn.fn_type_id.cc;
-
- if (fn_proto->is_export) {
- switch (fn_cc) {
- case CallingConventionAsync:
- add_node_error(g, fn_def_node,
- buf_sprintf("exported function cannot be async"));
- fn_table_entry->type_entry = g->builtin_types.entry_invalid;
- tld_fn->base.resolution = TldResolutionInvalid;
- return;
- case CallingConventionC:
- case CallingConventionCold:
- case CallingConventionNaked:
- case CallingConventionInterrupt:
- case CallingConventionSignal:
- case CallingConventionStdcall:
- case CallingConventionFastcall:
- case CallingConventionVectorcall:
- case CallingConventionThiscall:
- case CallingConventionAPCS:
- case CallingConventionAAPCS:
- case CallingConventionAAPCSVFP:
- add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name),
- GlobalLinkageIdStrong, fn_cc);
- break;
- case CallingConventionUnspecified:
- // An exported function without a specific calling
- // convention defaults to C
- add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name),
- GlobalLinkageIdStrong, CallingConventionC);
- break;
- }
- }
-
- if (!fn_table_entry->type_entry->data.fn.is_generic) {
- if (fn_def_node)
- g->fn_defs.append(fn_table_entry);
- }
-
- // if the calling convention implies that it cannot be async, we save that for later
- // and leave the value to be nullptr to indicate that we have not emitted possible
- // compile errors for improperly calling async functions.
- if (fn_cc == CallingConventionAsync) {
- fn_table_entry->inferred_async_node = fn_table_entry->proto_node;
- }
- } else if (source_node->type == NodeTypeTestDecl) {
- ZigFn *fn_table_entry = create_fn_raw(g, FnInlineAuto);
-
- get_fully_qualified_decl_name(g, &fn_table_entry->symbol_name, &tld_fn->base, true);
-
- tld_fn->fn_entry = fn_table_entry;
-
- fn_table_entry->proto_node = source_node;
- fn_table_entry->fndef_scope = create_fndef_scope(g, source_node, tld_fn->base.parent_scope, fn_table_entry);
- fn_table_entry->type_entry = get_test_fn_type(g);
- fn_table_entry->body_node = source_node->data.test_decl.body;
- fn_table_entry->is_test = true;
-
- g->fn_defs.append(fn_table_entry);
- g->test_fns.append(fn_table_entry);
-
- } else {
- zig_unreachable();
- }
-}
-
-static void resolve_decl_comptime(CodeGen *g, TldCompTime *tld_comptime) {
- assert(tld_comptime->base.source_node->type == NodeTypeCompTime);
- AstNode *expr_node = tld_comptime->base.source_node->data.comptime_expr.expr;
- analyze_const_value(g, tld_comptime->base.parent_scope, expr_node, g->builtin_types.entry_void,
- nullptr, UndefBad);
-}
-
-static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) {
- bool is_export = false;
- if (tld->id == TldIdVar) {
- assert(tld->source_node->type == NodeTypeVariableDeclaration);
- is_export = tld->source_node->data.variable_declaration.is_export;
- } else if (tld->id == TldIdFn) {
- assert(tld->source_node->type == NodeTypeFnProto);
- is_export = tld->source_node->data.fn_proto.is_export;
-
- if (!tld->source_node->data.fn_proto.is_extern &&
- tld->source_node->data.fn_proto.fn_def_node == nullptr)
- {
- add_node_error(g, tld->source_node, buf_sprintf("non-extern function has no body"));
- return;
- }
- if (!tld->source_node->data.fn_proto.is_extern &&
- tld->source_node->data.fn_proto.is_var_args)
- {
- add_node_error(g, tld->source_node, buf_sprintf("non-extern function is variadic"));
- return;
- }
- } else if (tld->id == TldIdUsingNamespace) {
- g->resolve_queue.append(tld);
- }
- if (is_export) {
- g->resolve_queue.append(tld);
-
- auto entry = g->exported_symbol_names.put_unique(tld->name, tld);
- if (entry) {
- AstNode *other_source_node = entry->value->source_node;
- ErrorMsg *msg = add_node_error(g, tld->source_node,
- buf_sprintf("exported symbol collision: '%s'", buf_ptr(tld->name)));
- add_error_note(g, msg, other_source_node, buf_sprintf("other symbol here"));
- }
- }
-
- if (tld->name != nullptr) {
- auto entry = decls_scope->decl_table.put_unique(tld->name, tld);
- if (entry) {
- Tld *other_tld = entry->value;
- if (other_tld->id == TldIdVar) {
- ZigVar *var = reinterpret_cast(other_tld)->var;
- if (var != nullptr && var->var_type != nullptr && type_is_invalid(var->var_type)) {
- return; // already reported compile error
- }
- }
- ErrorMsg *msg = add_node_error(g, tld->source_node, buf_sprintf("redefinition of '%s'", buf_ptr(tld->name)));
- add_error_note(g, msg, other_tld->source_node, buf_sprintf("previous definition is here"));
- return;
- }
-
- ZigType *type;
- if (get_primitive_type(g, tld->name, &type) != ErrorPrimitiveTypeNotFound) {
- add_node_error(g, tld->source_node,
- buf_sprintf("declaration shadows primitive type '%s'", buf_ptr(tld->name)));
- }
- }
-}
-
-static void preview_test_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope) {
- assert(node->type == NodeTypeTestDecl);
-
- if (!g->is_test_build)
- return;
-
- ZigType *import = get_scope_import(&decls_scope->base);
- if (import->data.structure.root_struct->package != g->main_pkg)
- return;
-
- Buf *decl_name_buf = node->data.test_decl.name;
-
- Buf *test_name = g->test_name_prefix ?
- buf_sprintf("%s%s", buf_ptr(g->test_name_prefix), buf_ptr(decl_name_buf)) : decl_name_buf;
-
- if (g->test_filter != nullptr && strstr(buf_ptr(test_name), buf_ptr(g->test_filter)) == nullptr) {
- return;
- }
-
- TldFn *tld_fn = heap::c_allocator.create();
- init_tld(&tld_fn->base, TldIdFn, test_name, VisibModPrivate, node, &decls_scope->base);
- g->resolve_queue.append(&tld_fn->base);
-}
-
-static void preview_comptime_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope) {
- assert(node->type == NodeTypeCompTime);
-
- TldCompTime *tld_comptime = heap::c_allocator.create();
- init_tld(&tld_comptime->base, TldIdCompTime, nullptr, VisibModPrivate, node, &decls_scope->base);
- g->resolve_queue.append(&tld_comptime->base);
-}
-
-void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node,
- Scope *parent_scope)
-{
- tld->id = id;
- tld->name = name;
- tld->visib_mod = visib_mod;
- tld->source_node = source_node;
- tld->import = source_node ? source_node->owner : nullptr;
- tld->parent_scope = parent_scope;
-}
-
-void update_compile_var(CodeGen *g, Buf *name, ZigValue *value) {
- ScopeDecls *builtin_scope = get_container_scope(g->compile_var_import);
- Tld *tld = find_container_decl(g, builtin_scope, name);
- assert(tld != nullptr);
- resolve_top_level_decl(g, tld, tld->source_node, false);
- assert(tld->id == TldIdVar && tld->resolution == TldResolutionOk);
- TldVar *tld_var = (TldVar *)tld;
- copy_const_val(g, tld_var->var->const_value, value);
- tld_var->var->var_type = value->type;
- tld_var->var->align_bytes = get_abi_alignment(g, value->type);
-}
-
-void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
- switch (node->type) {
- case NodeTypeContainerDecl:
- for (size_t i = 0; i < node->data.container_decl.decls.length; i += 1) {
- AstNode *child = node->data.container_decl.decls.at(i);
- scan_decls(g, decls_scope, child);
- }
- break;
- case NodeTypeFnDef:
- scan_decls(g, decls_scope, node->data.fn_def.fn_proto);
- break;
- case NodeTypeVariableDeclaration:
- {
- Buf *name = node->data.variable_declaration.symbol;
- VisibMod visib_mod = node->data.variable_declaration.visib_mod;
- TldVar *tld_var = heap::c_allocator.create();
- init_tld(&tld_var->base, TldIdVar, name, visib_mod, node, &decls_scope->base);
- tld_var->extern_lib_name = node->data.variable_declaration.lib_name;
- add_top_level_decl(g, decls_scope, &tld_var->base);
- break;
- }
- case NodeTypeFnProto:
- {
- // if the name is missing, we immediately announce an error
- Buf *fn_name = node->data.fn_proto.name;
- if (fn_name == nullptr) {
- add_node_error(g, node, buf_sprintf("missing function name"));
- break;
- }
-
- VisibMod visib_mod = node->data.fn_proto.visib_mod;
- TldFn *tld_fn = heap::c_allocator.create();
- init_tld(&tld_fn->base, TldIdFn, fn_name, visib_mod, node, &decls_scope->base);
- tld_fn->extern_lib_name = node->data.fn_proto.lib_name;
- add_top_level_decl(g, decls_scope, &tld_fn->base);
-
- break;
- }
- case NodeTypeUsingNamespace: {
- VisibMod visib_mod = node->data.using_namespace.visib_mod;
- TldUsingNamespace *tld_using_namespace = heap::c_allocator.create();
- init_tld(&tld_using_namespace->base, TldIdUsingNamespace, nullptr, visib_mod, node, &decls_scope->base);
- add_top_level_decl(g, decls_scope, &tld_using_namespace->base);
- decls_scope->use_decls.append(tld_using_namespace);
- break;
- }
- case NodeTypeTestDecl:
- preview_test_decl(g, node, decls_scope);
- break;
- case NodeTypeCompTime:
- preview_comptime_decl(g, node, decls_scope);
- break;
- case NodeTypeNoSuspend:
- case NodeTypeParamDecl:
- case NodeTypeReturnExpr:
- case NodeTypeDefer:
- case NodeTypeBlock:
- case NodeTypeGroupedExpr:
- case NodeTypeBinOpExpr:
- case NodeTypeCatchExpr:
- case NodeTypeFnCallExpr:
- case NodeTypeArrayAccessExpr:
- case NodeTypeSliceExpr:
- case NodeTypeFloatLiteral:
- case NodeTypeIntLiteral:
- case NodeTypeStringLiteral:
- case NodeTypeCharLiteral:
- case NodeTypeBoolLiteral:
- case NodeTypeNullLiteral:
- case NodeTypeUndefinedLiteral:
- case NodeTypeSymbol:
- case NodeTypePrefixOpExpr:
- case NodeTypePointerType:
- case NodeTypeIfBoolExpr:
- case NodeTypeWhileExpr:
- case NodeTypeForExpr:
- case NodeTypeSwitchExpr:
- case NodeTypeSwitchProng:
- case NodeTypeSwitchRange:
- case NodeTypeBreak:
- case NodeTypeContinue:
- case NodeTypeUnreachable:
- case NodeTypeAsmExpr:
- case NodeTypeFieldAccessExpr:
- case NodeTypePtrDeref:
- case NodeTypeUnwrapOptional:
- case NodeTypeStructField:
- case NodeTypeContainerInitExpr:
- case NodeTypeStructValueField:
- case NodeTypeArrayType:
- case NodeTypeInferredArrayType:
- case NodeTypeErrorType:
- case NodeTypeIfErrorExpr:
- case NodeTypeIfOptional:
- case NodeTypeErrorSetDecl:
- case NodeTypeResume:
- case NodeTypeAwaitExpr:
- case NodeTypeSuspend:
- case NodeTypeEnumLiteral:
- case NodeTypeAnyFrameType:
- case NodeTypeErrorSetField:
- case NodeTypeAnyTypeField:
- zig_unreachable();
- }
-}
-
-static Error resolve_decl_container(CodeGen *g, TldContainer *tld_container) {
- ZigType *type_entry = tld_container->type_entry;
- assert(type_entry);
-
- switch (type_entry->id) {
- case ZigTypeIdStruct:
- return resolve_struct_type(g, tld_container->type_entry);
- case ZigTypeIdEnum:
- return resolve_enum_zero_bits(g, tld_container->type_entry);
- case ZigTypeIdUnion:
- return resolve_union_type(g, tld_container->type_entry);
- default:
- zig_unreachable();
- }
-}
-
-ZigType *validate_var_type(CodeGen *g, AstNodeVariableDeclaration *source_node, ZigType *type_entry) {
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- return g->builtin_types.entry_invalid;
- case ZigTypeIdOpaque:
- if (source_node->is_extern)
- return type_entry;
- ZIG_FALLTHROUGH;
- case ZigTypeIdUnreachable:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- add_node_error(g, source_node->type, buf_sprintf("variable of type '%s' not allowed",
- buf_ptr(&type_entry->name)));
- return g->builtin_types.entry_invalid;
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdMetaType:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdInt:
- case ZigTypeIdFloat:
- case ZigTypeIdPointer:
- case ZigTypeIdArray:
- case ZigTypeIdStruct:
- case ZigTypeIdOptional:
- case ZigTypeIdErrorUnion:
- case ZigTypeIdErrorSet:
- case ZigTypeIdEnum:
- case ZigTypeIdUnion:
- case ZigTypeIdFn:
- case ZigTypeIdBoundFn:
- case ZigTypeIdVector:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- return type_entry;
- }
- zig_unreachable();
-}
-
-// Set name to nullptr to make the variable anonymous (not visible to programmer).
-// TODO merge with definition of add_local_var in ir.cpp
-ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name,
- bool is_const, ZigValue *const_value, Tld *src_tld, ZigType *var_type)
-{
- Error err;
- assert(const_value != nullptr);
- assert(var_type != nullptr);
-
- ZigVar *variable_entry = heap::c_allocator.create();
- variable_entry->const_value = const_value;
- variable_entry->var_type = var_type;
- variable_entry->parent_scope = parent_scope;
- variable_entry->shadowable = false;
- variable_entry->src_arg_index = SIZE_MAX;
-
- assert(name);
- variable_entry->name = strdup(buf_ptr(name));
-
- if ((err = type_resolve(g, var_type, ResolveStatusAlignmentKnown))) {
- variable_entry->var_type = g->builtin_types.entry_invalid;
- } else {
- variable_entry->align_bytes = get_abi_alignment(g, var_type);
-
- ZigVar *existing_var = find_variable(g, parent_scope, name, nullptr);
- if (existing_var && !existing_var->shadowable) {
- if (existing_var->var_type == nullptr || !type_is_invalid(existing_var->var_type)) {
- ErrorMsg *msg = add_node_error(g, source_node,
- buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
- add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
- }
- variable_entry->var_type = g->builtin_types.entry_invalid;
- } else {
- ZigType *type;
- if (get_primitive_type(g, name, &type) != ErrorPrimitiveTypeNotFound) {
- add_node_error(g, source_node,
- buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name)));
- variable_entry->var_type = g->builtin_types.entry_invalid;
- } else {
- Scope *search_scope = nullptr;
- if (src_tld == nullptr) {
- search_scope = parent_scope;
- } else if (src_tld->parent_scope != nullptr && src_tld->parent_scope->parent != nullptr) {
- search_scope = src_tld->parent_scope->parent;
- }
- if (search_scope != nullptr) {
- Tld *tld = find_decl(g, search_scope, name);
- if (tld != nullptr && tld != src_tld) {
- bool want_err_msg = true;
- if (tld->id == TldIdVar) {
- ZigVar *var = reinterpret_cast(tld)->var;
- if (var != nullptr && var->var_type != nullptr && type_is_invalid(var->var_type)) {
- want_err_msg = false;
- }
- }
- if (want_err_msg) {
- ErrorMsg *msg = add_node_error(g, source_node,
- buf_sprintf("redefinition of '%s'", buf_ptr(name)));
- add_error_note(g, msg, tld->source_node, buf_sprintf("previous definition is here"));
- }
- variable_entry->var_type = g->builtin_types.entry_invalid;
- }
- }
- }
- }
- }
-
- Scope *child_scope;
- if (source_node && source_node->type == NodeTypeParamDecl) {
- child_scope = create_var_scope(g, source_node, parent_scope, variable_entry);
- } else {
- // it's already in the decls table
- child_scope = parent_scope;
- }
-
-
- variable_entry->src_is_const = is_const;
- variable_entry->gen_is_const = is_const;
- variable_entry->decl_node = source_node;
- variable_entry->child_scope = child_scope;
-
-
- return variable_entry;
-}
-
-static void validate_export_var_type(CodeGen *g, ZigType* type, AstNode *source_node) {
- switch (type->id) {
- case ZigTypeIdMetaType:
- add_node_error(g, source_node, buf_sprintf("cannot export variable of type 'type'"));
- break;
- default:
- break;
- }
-}
-
-static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) {
- AstNode *source_node = tld_var->base.source_node;
- AstNodeVariableDeclaration *var_decl = &source_node->data.variable_declaration;
-
- bool is_const = var_decl->is_const;
- bool is_extern = var_decl->is_extern;
- bool is_export = var_decl->is_export;
- bool is_thread_local = var_decl->threadlocal_tok != nullptr;
-
- ZigType *explicit_type = nullptr;
- if (var_decl->type) {
- if (tld_var->analyzing_type) {
- add_node_error(g, var_decl->type,
- buf_sprintf("type of '%s' depends on itself", buf_ptr(tld_var->base.name)));
- explicit_type = g->builtin_types.entry_invalid;
- } else {
- tld_var->analyzing_type = true;
- ZigType *proposed_type = analyze_type_expr(g, tld_var->base.parent_scope, var_decl->type);
- explicit_type = validate_var_type(g, var_decl, proposed_type);
- }
- }
-
- assert(!is_export || !is_extern);
-
- ZigValue *init_value = nullptr;
-
- // TODO more validation for types that can't be used for export/extern variables
- ZigType *implicit_type = nullptr;
- if (explicit_type != nullptr && type_is_invalid(explicit_type)) {
- implicit_type = explicit_type;
- } else if (var_decl->expr) {
- init_value = analyze_const_value(g, tld_var->base.parent_scope, var_decl->expr, explicit_type,
- var_decl->symbol, allow_lazy ? LazyOk : UndefOk);
- assert(init_value);
- implicit_type = init_value->type;
-
- if (implicit_type->id == ZigTypeIdUnreachable) {
- add_node_error(g, source_node, buf_sprintf("variable initialization is unreachable"));
- implicit_type = g->builtin_types.entry_invalid;
- } else if ((!is_const || is_extern) &&
- (implicit_type->id == ZigTypeIdComptimeFloat ||
- implicit_type->id == ZigTypeIdComptimeInt ||
- implicit_type->id == ZigTypeIdEnumLiteral))
- {
- add_node_error(g, source_node, buf_sprintf("unable to infer variable type"));
- implicit_type = g->builtin_types.entry_invalid;
- } else if (implicit_type->id == ZigTypeIdNull) {
- add_node_error(g, source_node, buf_sprintf("unable to infer variable type"));
- implicit_type = g->builtin_types.entry_invalid;
- } else if (implicit_type->id == ZigTypeIdMetaType && !is_const) {
- add_node_error(g, source_node, buf_sprintf("variable of type 'type' must be constant"));
- implicit_type = g->builtin_types.entry_invalid;
- }
- assert(implicit_type->id == ZigTypeIdInvalid || init_value->special != ConstValSpecialRuntime);
- } else if (!is_extern) {
- add_node_error(g, source_node, buf_sprintf("variables must be initialized"));
- implicit_type = g->builtin_types.entry_invalid;
- } else if (explicit_type == nullptr) {
- // extern variable without explicit type
- add_node_error(g, source_node, buf_sprintf("unable to infer variable type"));
- implicit_type = g->builtin_types.entry_invalid;
- }
-
- ZigType *type = explicit_type ? explicit_type : implicit_type;
- assert(type != nullptr); // should have been caught by the parser
-
- ZigValue *init_val = (init_value != nullptr) ? init_value : create_const_runtime(g, type);
-
- tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol,
- is_const, init_val, &tld_var->base, type);
- tld_var->var->is_thread_local = is_thread_local;
-
- if (implicit_type != nullptr && type_is_invalid(implicit_type)) {
- tld_var->var->var_type = g->builtin_types.entry_invalid;
- }
-
- if (var_decl->align_expr != nullptr) {
- if (!analyze_const_align(g, tld_var->base.parent_scope, var_decl->align_expr, &tld_var->var->align_bytes)) {
- tld_var->var->var_type = g->builtin_types.entry_invalid;
- }
- }
-
- if (var_decl->section_expr != nullptr) {
- if (!analyze_const_string(g, tld_var->base.parent_scope, var_decl->section_expr, &tld_var->var->section_name)) {
- tld_var->var->section_name = nullptr;
- }
- }
-
- if (is_thread_local && is_const) {
- add_node_error(g, source_node, buf_sprintf("threadlocal variable cannot be constant"));
- }
-
- if (is_export) {
- validate_export_var_type(g, type, source_node);
- add_var_export(g, tld_var->var, tld_var->var->name, GlobalLinkageIdStrong);
- }
-
- if (is_extern) {
- g->external_symbol_names.put_unique(tld_var->base.name, &tld_var->base);
- }
-
- g->global_vars.append(tld_var);
-}
-
-static void add_symbols_from_container(CodeGen *g, TldUsingNamespace *src_using_namespace,
- TldUsingNamespace *dst_using_namespace, ScopeDecls* dest_decls_scope)
-{
- if (src_using_namespace->base.resolution == TldResolutionUnresolved ||
- src_using_namespace->base.resolution == TldResolutionResolving)
- {
- assert(src_using_namespace->base.parent_scope->id == ScopeIdDecls);
- ScopeDecls *src_decls_scope = (ScopeDecls *)src_using_namespace->base.parent_scope;
- preview_use_decl(g, src_using_namespace, src_decls_scope);
- if (src_using_namespace != dst_using_namespace) {
- resolve_use_decl(g, src_using_namespace, src_decls_scope);
- }
- }
-
- ZigValue *use_expr = src_using_namespace->using_namespace_value;
- if (type_is_invalid(use_expr->type)) {
- dest_decls_scope->any_imports_failed = true;
- return;
- }
-
- dst_using_namespace->base.resolution = TldResolutionOk;
-
- assert(use_expr->special != ConstValSpecialRuntime);
-
- // The source scope for the imported symbols
- ScopeDecls *src_scope = get_container_scope(use_expr->data.x_type);
- // The top-level container where the symbols are defined, it's used in the
- // loop below in order to exclude the ones coming from an import statement
- ZigType *src_import = get_scope_import(&src_scope->base);
- assert(src_import != nullptr);
-
- if (src_scope->any_imports_failed) {
- dest_decls_scope->any_imports_failed = true;
- }
-
- auto it = src_scope->decl_table.entry_iterator();
- for (;;) {
- auto *entry = it.next();
- if (!entry)
- break;
-
- Buf *target_tld_name = entry->key;
- Tld *target_tld = entry->value;
-
- if (target_tld->visib_mod == VisibModPrivate) {
- continue;
- }
-
- if (target_tld->import != src_import) {
- continue;
- }
-
- auto existing_entry = dest_decls_scope->decl_table.put_unique(target_tld_name, target_tld);
- if (existing_entry) {
- Tld *existing_decl = existing_entry->value;
- if (existing_decl != target_tld) {
- ErrorMsg *msg = add_node_error(g, dst_using_namespace->base.source_node,
- buf_sprintf("import of '%s' overrides existing definition",
- buf_ptr(target_tld_name)));
- add_error_note(g, msg, existing_decl->source_node, buf_sprintf("previous definition here"));
- add_error_note(g, msg, target_tld->source_node, buf_sprintf("imported definition here"));
- }
- }
- }
-
- for (size_t i = 0; i < src_scope->use_decls.length; i += 1) {
- TldUsingNamespace *tld_using_namespace = src_scope->use_decls.at(i);
- if (tld_using_namespace->base.visib_mod != VisibModPrivate)
- add_symbols_from_container(g, tld_using_namespace, dst_using_namespace, dest_decls_scope);
- }
-}
-
-static void resolve_use_decl(CodeGen *g, TldUsingNamespace *tld_using_namespace, ScopeDecls *dest_decls_scope) {
- if (tld_using_namespace->base.resolution == TldResolutionOk ||
- tld_using_namespace->base.resolution == TldResolutionInvalid)
- {
- return;
- }
- add_symbols_from_container(g, tld_using_namespace, tld_using_namespace, dest_decls_scope);
-}
-
-static void preview_use_decl(CodeGen *g, TldUsingNamespace *using_namespace, ScopeDecls *dest_decls_scope) {
- if (using_namespace->base.resolution == TldResolutionOk ||
- using_namespace->base.resolution == TldResolutionInvalid ||
- using_namespace->using_namespace_value != nullptr)
- {
- return;
- }
-
- using_namespace->base.resolution = TldResolutionResolving;
- assert(using_namespace->base.source_node->type == NodeTypeUsingNamespace);
- ZigValue *result = analyze_const_value(g, &dest_decls_scope->base,
- using_namespace->base.source_node->data.using_namespace.expr, g->builtin_types.entry_type,
- nullptr, UndefBad);
- using_namespace->using_namespace_value = result;
-
- if (type_is_invalid(result->type)) {
- dest_decls_scope->any_imports_failed = true;
- using_namespace->base.resolution = TldResolutionInvalid;
- using_namespace->using_namespace_value = g->invalid_inst_gen->value;
- return;
- }
-
- if (!is_container(result->data.x_type)) {
- add_node_error(g, using_namespace->base.source_node,
- buf_sprintf("expected struct, enum, or union; found '%s'", buf_ptr(&result->data.x_type->name)));
- dest_decls_scope->any_imports_failed = true;
- using_namespace->base.resolution = TldResolutionInvalid;
- using_namespace->using_namespace_value = g->invalid_inst_gen->value;
- return;
- }
-}
-
-void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node, bool allow_lazy) {
- bool want_resolve_lazy = tld->resolution == TldResolutionOkLazy && !allow_lazy;
- if (tld->resolution != TldResolutionUnresolved && !want_resolve_lazy)
- return;
-
- tld->resolution = TldResolutionResolving;
- update_progress_display(g);
-
- switch (tld->id) {
- case TldIdVar: {
- TldVar *tld_var = (TldVar *)tld;
- if (want_resolve_lazy) {
- ir_resolve_lazy(g, source_node, tld_var->var->const_value);
- } else {
- resolve_decl_var(g, tld_var, allow_lazy);
- }
- tld->resolution = allow_lazy ? TldResolutionOkLazy : TldResolutionOk;
- break;
- }
- case TldIdFn: {
- TldFn *tld_fn = (TldFn *)tld;
- resolve_decl_fn(g, tld_fn);
-
- tld->resolution = TldResolutionOk;
- break;
- }
- case TldIdContainer: {
- TldContainer *tld_container = (TldContainer *)tld;
- resolve_decl_container(g, tld_container);
-
- tld->resolution = TldResolutionOk;
- break;
- }
- case TldIdCompTime: {
- TldCompTime *tld_comptime = (TldCompTime *)tld;
- resolve_decl_comptime(g, tld_comptime);
-
- tld->resolution = TldResolutionOk;
- break;
- }
- case TldIdUsingNamespace: {
- TldUsingNamespace *tld_using_namespace = (TldUsingNamespace *)tld;
- assert(tld_using_namespace->base.parent_scope->id == ScopeIdDecls);
- ScopeDecls *dest_decls_scope = (ScopeDecls *)tld_using_namespace->base.parent_scope;
- preview_use_decl(g, tld_using_namespace, dest_decls_scope);
- resolve_use_decl(g, tld_using_namespace, dest_decls_scope);
-
- tld->resolution = TldResolutionOk;
- break;
- }
- }
-
- if (g->trace_err != nullptr && source_node != nullptr && !source_node->already_traced_this_node) {
- g->trace_err = add_error_note(g, g->trace_err, source_node, buf_create_from_str("referenced here"));
- source_node->already_traced_this_node = true;
- }
-}
-
-Tld *find_container_decl(CodeGen *g, ScopeDecls *decls_scope, Buf *name) {
- // resolve all the using_namespace decls
- for (size_t i = 0; i < decls_scope->use_decls.length; i += 1) {
- TldUsingNamespace *tld_using_namespace = decls_scope->use_decls.at(i);
- if (tld_using_namespace->base.resolution == TldResolutionUnresolved) {
- preview_use_decl(g, tld_using_namespace, decls_scope);
- resolve_use_decl(g, tld_using_namespace, decls_scope);
- }
- }
-
- auto entry = decls_scope->decl_table.maybe_get(name);
- return (entry == nullptr) ? nullptr : entry->value;
-}
-
-Tld *find_decl(CodeGen *g, Scope *scope, Buf *name) {
- while (scope) {
- if (scope->id == ScopeIdDecls) {
- ScopeDecls *decls_scope = (ScopeDecls *)scope;
-
- Tld *result = find_container_decl(g, decls_scope, name);
- if (result != nullptr)
- return result;
- }
- scope = scope->parent;
- }
- return nullptr;
-}
-
-ZigVar *find_variable(CodeGen *g, Scope *scope, Buf *name, ScopeFnDef **crossed_fndef_scope) {
- ScopeFnDef *my_crossed_fndef_scope = nullptr;
- while (scope) {
- if (scope->id == ScopeIdVarDecl) {
- ScopeVarDecl *var_scope = (ScopeVarDecl *)scope;
- if (buf_eql_str(name, var_scope->var->name)) {
- if (crossed_fndef_scope != nullptr)
- *crossed_fndef_scope = my_crossed_fndef_scope;
- return var_scope->var;
- }
- } else if (scope->id == ScopeIdDecls) {
- ScopeDecls *decls_scope = (ScopeDecls *)scope;
- auto entry = decls_scope->decl_table.maybe_get(name);
- if (entry) {
- Tld *tld = entry->value;
- if (tld->id == TldIdVar) {
- TldVar *tld_var = (TldVar *)tld;
- if (tld_var->var) {
- if (crossed_fndef_scope != nullptr)
- *crossed_fndef_scope = nullptr;
- return tld_var->var;
- }
- }
- }
- } else if (scope->id == ScopeIdFnDef) {
- my_crossed_fndef_scope = (ScopeFnDef *)scope;
- }
- scope = scope->parent;
- }
-
- return nullptr;
-}
-
-ZigFn *scope_fn_entry(Scope *scope) {
- while (scope) {
- if (scope->id == ScopeIdFnDef) {
- ScopeFnDef *fn_scope = (ScopeFnDef *)scope;
- return fn_scope->fn_entry;
- }
- scope = scope->parent;
- }
- return nullptr;
-}
-
-ZigPackage *scope_package(Scope *scope) {
- ZigType *import = get_scope_import(scope);
- assert(is_top_level_struct(import));
- return import->data.structure.root_struct->package;
-}
-
-TypeEnumField *find_enum_type_field(ZigType *enum_type, Buf *name) {
- assert(enum_type->id == ZigTypeIdEnum);
- if (enum_type->data.enumeration.src_field_count == 0)
- return nullptr;
- auto entry = enum_type->data.enumeration.fields_by_name.maybe_get(name);
- if (entry == nullptr)
- return nullptr;
- return entry->value;
-}
-
-TypeStructField *find_struct_type_field(ZigType *type_entry, Buf *name) {
- assert(type_entry->id == ZigTypeIdStruct);
- if (type_entry->data.structure.resolve_status == ResolveStatusBeingInferred) {
- for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
- TypeStructField *field = type_entry->data.structure.fields[i];
- if (buf_eql_buf(field->name, name))
- return field;
- }
- return nullptr;
- } else {
- assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown));
- if (type_entry->data.structure.src_field_count == 0)
- return nullptr;
- auto entry = type_entry->data.structure.fields_by_name.maybe_get(name);
- if (entry == nullptr)
- return nullptr;
- return entry->value;
- }
-}
-
-TypeUnionField *find_union_type_field(ZigType *type_entry, Buf *name) {
- assert(type_entry->id == ZigTypeIdUnion);
- assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown));
- if (type_entry->data.unionation.src_field_count == 0)
- return nullptr;
- auto entry = type_entry->data.unionation.fields_by_name.maybe_get(name);
- if (entry == nullptr)
- return nullptr;
- return entry->value;
-}
-
-TypeUnionField *find_union_field_by_tag(ZigType *type_entry, const BigInt *tag) {
- assert(type_entry->id == ZigTypeIdUnion);
- assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown));
- for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) {
- TypeUnionField *field = &type_entry->data.unionation.fields[i];
- if (bigint_cmp(&field->enum_field->value, tag) == CmpEQ) {
- return field;
- }
- }
- return nullptr;
-}
-
-TypeEnumField *find_enum_field_by_tag(ZigType *enum_type, const BigInt *tag) {
- assert(type_is_resolved(enum_type, ResolveStatusZeroBitsKnown));
- for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) {
- TypeEnumField *field = &enum_type->data.enumeration.fields[i];
- if (bigint_cmp(&field->value, tag) == CmpEQ) {
- return field;
- }
- }
- return nullptr;
-}
-
-
-bool is_container(ZigType *type_entry) {
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdStruct:
- return type_entry->data.structure.special != StructSpecialSlice;
- case ZigTypeIdEnum:
- case ZigTypeIdUnion:
- return true;
- case ZigTypeIdPointer:
- case ZigTypeIdMetaType:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdUnreachable:
- case ZigTypeIdInt:
- case ZigTypeIdFloat:
- case ZigTypeIdArray:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdOptional:
- case ZigTypeIdErrorUnion:
- case ZigTypeIdErrorSet:
- case ZigTypeIdFn:
- case ZigTypeIdBoundFn:
- case ZigTypeIdOpaque:
- case ZigTypeIdVector:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- return false;
- }
- zig_unreachable();
-}
-
-bool is_ref(ZigType *type_entry) {
- return type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.ptr_len == PtrLenSingle;
-}
-
-bool is_array_ref(ZigType *type_entry) {
- ZigType *array = is_ref(type_entry) ?
- type_entry->data.pointer.child_type : type_entry;
- return array->id == ZigTypeIdArray;
-}
-
-bool is_container_ref(ZigType *parent_ty) {
- ZigType *ty = is_ref(parent_ty) ? parent_ty->data.pointer.child_type : parent_ty;
- return is_slice(ty) || is_container(ty);
-}
-
-ZigType *container_ref_type(ZigType *type_entry) {
- assert(is_container_ref(type_entry));
- return is_ref(type_entry) ?
- type_entry->data.pointer.child_type : type_entry;
-}
-
-ZigType *get_src_ptr_type(ZigType *type) {
- if (type->id == ZigTypeIdPointer) return type;
- if (type->id == ZigTypeIdFn) return type;
- if (type->id == ZigTypeIdAnyFrame) return type;
- if (type->id == ZigTypeIdOptional) {
- if (type->data.maybe.child_type->id == ZigTypeIdPointer) {
- return type->data.maybe.child_type->data.pointer.allow_zero ? nullptr : type->data.maybe.child_type;
- }
- if (type->data.maybe.child_type->id == ZigTypeIdFn) return type->data.maybe.child_type;
- if (type->data.maybe.child_type->id == ZigTypeIdAnyFrame) return type->data.maybe.child_type;
- }
- return nullptr;
-}
-
-Error get_codegen_ptr_type(CodeGen *g, ZigType *type, ZigType **result) {
- Error err;
-
- ZigType *ty = get_src_ptr_type(type);
- if (ty == nullptr) {
- *result = nullptr;
- return ErrorNone;
- }
-
- bool has_bits;
- if ((err = type_has_bits2(g, ty, &has_bits))) return err;
- if (!has_bits) {
- *result = nullptr;
- return ErrorNone;
- }
-
- *result = ty;
- return ErrorNone;
-}
-
-ZigType *get_codegen_ptr_type_bail(CodeGen *g, ZigType *type) {
- Error err;
- ZigType *result;
- if ((err = get_codegen_ptr_type(g, type, &result))) {
- codegen_report_errors_and_exit(g);
- }
- return result;
-}
-
-bool type_is_nonnull_ptr(CodeGen *g, ZigType *type) {
- Error err;
- bool result;
- if ((err = type_is_nonnull_ptr2(g, type, &result))) {
- codegen_report_errors_and_exit(g);
- }
- return result;
-}
-
-Error type_is_nonnull_ptr2(CodeGen *g, ZigType *type, bool *result) {
- Error err;
- ZigType *ptr_type;
- if ((err = get_codegen_ptr_type(g, type, &ptr_type))) return err;
- *result = ptr_type == type && !ptr_allows_addr_zero(type);
- return ErrorNone;
-}
-
-static uint32_t get_async_frame_align_bytes(CodeGen *g) {
- uint32_t a = g->pointer_size_bytes * 2;
- // promises have at least alignment 8 so that we can have 3 extra bits when doing atomicrmw
- if (a < 8) a = 8;
- return a;
-}
-
-uint32_t get_ptr_align(CodeGen *g, ZigType *type) {
- ZigType *ptr_type;
- if (type->id == ZigTypeIdStruct) {
- assert(type->data.structure.special == StructSpecialSlice);
- TypeStructField *ptr_field = type->data.structure.fields[slice_ptr_index];
- ptr_type = resolve_struct_field_type(g, ptr_field);
- } else {
- ptr_type = get_src_ptr_type(type);
- }
- if (ptr_type->id == ZigTypeIdPointer) {
- return (ptr_type->data.pointer.explicit_alignment == 0) ?
- get_abi_alignment(g, ptr_type->data.pointer.child_type) : ptr_type->data.pointer.explicit_alignment;
- } else if (ptr_type->id == ZigTypeIdFn) {
- // I tried making this use LLVMABIAlignmentOfType but it trips this assertion in LLVM:
- // "Cannot getTypeInfo() on a type that is unsized!"
- // when getting the alignment of `?fn() callconv(.C) void`.
- // See http://lists.llvm.org/pipermail/llvm-dev/2018-September/126142.html
- return (ptr_type->data.fn.fn_type_id.alignment == 0) ? 1 : ptr_type->data.fn.fn_type_id.alignment;
- } else if (ptr_type->id == ZigTypeIdAnyFrame) {
- return get_async_frame_align_bytes(g);
- } else {
- zig_unreachable();
- }
-}
-
-bool get_ptr_const(CodeGen *g, ZigType *type) {
- ZigType *ptr_type;
- if (type->id == ZigTypeIdStruct) {
- assert(type->data.structure.special == StructSpecialSlice);
- TypeStructField *ptr_field = type->data.structure.fields[slice_ptr_index];
- ptr_type = resolve_struct_field_type(g, ptr_field);
- } else {
- ptr_type = get_src_ptr_type(type);
- }
- if (ptr_type->id == ZigTypeIdPointer) {
- return ptr_type->data.pointer.is_const;
- } else if (ptr_type->id == ZigTypeIdFn) {
- return true;
- } else if (ptr_type->id == ZigTypeIdAnyFrame) {
- return true;
- } else {
- zig_unreachable();
- }
-}
-
-AstNode *get_param_decl_node(ZigFn *fn_entry, size_t index) {
- if (fn_entry->param_source_nodes)
- return fn_entry->param_source_nodes[index];
- else if (fn_entry->proto_node)
- return fn_entry->proto_node->data.fn_proto.params.at(index);
- else
- return nullptr;
-}
-
-static Error define_local_param_variables(CodeGen *g, ZigFn *fn_table_entry) {
- Error err;
- ZigType *fn_type = fn_table_entry->type_entry;
- assert(!fn_type->data.fn.is_generic);
- FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
- for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
- FnTypeParamInfo *param_info = &fn_type_id->param_info[i];
- AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
- Buf *param_name;
- bool is_var_args = param_decl_node && param_decl_node->data.param_decl.is_var_args;
- if (param_decl_node && !is_var_args) {
- param_name = param_decl_node->data.param_decl.name;
- } else {
- param_name = buf_sprintf("arg%" ZIG_PRI_usize "", i);
- }
- if (param_name == nullptr) {
- continue;
- }
-
- ZigType *param_type = param_info->type;
- if ((err = type_resolve(g, param_type, ResolveStatusSizeKnown))) {
- return err;
- }
-
- bool is_noalias = param_info->is_noalias;
- if (is_noalias) {
- ZigType *ptr_type;
- if ((err = get_codegen_ptr_type(g, param_type, &ptr_type))) return err;
- if (ptr_type == nullptr) {
- add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));
- }
- }
-
- ZigVar *var = add_variable(g, param_decl_node, fn_table_entry->child_scope,
- param_name, true, create_const_runtime(g, param_type), nullptr, param_type);
- var->src_arg_index = i;
- fn_table_entry->child_scope = var->child_scope;
- var->shadowable = var->shadowable || is_var_args;
-
- if (type_has_bits(g, param_type)) {
- fn_table_entry->variable_list.append(var);
- }
- }
-
- return ErrorNone;
-}
-
-bool resolve_inferred_error_set(CodeGen *g, ZigType *err_set_type, AstNode *source_node) {
- assert(err_set_type->id == ZigTypeIdErrorSet);
- ZigFn *infer_fn = err_set_type->data.error_set.infer_fn;
- if (infer_fn != nullptr && err_set_type->data.error_set.incomplete) {
- if (infer_fn->anal_state == FnAnalStateInvalid) {
- return false;
- } else if (infer_fn->anal_state == FnAnalStateReady) {
- analyze_fn_body(g, infer_fn);
- if (infer_fn->anal_state == FnAnalStateInvalid ||
- err_set_type->data.error_set.incomplete)
- {
- assert(g->errors.length != 0);
- return false;
- }
- } else {
- add_node_error(g, source_node,
- buf_sprintf("cannot resolve inferred error set '%s': function '%s' not fully analyzed yet",
- buf_ptr(&err_set_type->name), buf_ptr(&err_set_type->data.error_set.infer_fn->symbol_name)));
- return false;
- }
- }
- return true;
-}
-
-static void resolve_async_fn_frame(CodeGen *g, ZigFn *fn) {
- ZigType *frame_type = get_fn_frame_type(g, fn);
- Error err;
- if ((err = type_resolve(g, frame_type, ResolveStatusSizeKnown))) {
- if (g->trace_err != nullptr && frame_type->data.frame.resolve_loop_src_node != nullptr &&
- !frame_type->data.frame.reported_loop_err)
- {
- frame_type->data.frame.reported_loop_err = true;
- g->trace_err = add_error_note(g, g->trace_err, frame_type->data.frame.resolve_loop_src_node,
- buf_sprintf("when analyzing type '%s' here", buf_ptr(&frame_type->name)));
- }
- fn->anal_state = FnAnalStateInvalid;
- return;
- }
-}
-
-bool fn_is_async(ZigFn *fn) {
- assert(fn->inferred_async_node != nullptr);
- assert(fn->inferred_async_node != inferred_async_checking);
- return fn->inferred_async_node != inferred_async_none;
-}
-
-void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn) {
- assert(fn->inferred_async_node != nullptr);
- assert(fn->inferred_async_node != inferred_async_checking);
- assert(fn->inferred_async_node != inferred_async_none);
- if (fn->inferred_async_fn != nullptr) {
- ErrorMsg *new_msg;
- if (fn->inferred_async_node->type == NodeTypeAwaitExpr) {
- new_msg = add_error_note(g, msg, fn->inferred_async_node,
- buf_create_from_str("await here is a suspend point"));
- } else {
- new_msg = add_error_note(g, msg, fn->inferred_async_node,
- buf_sprintf("async function call here"));
- }
- return add_async_error_notes(g, new_msg, fn->inferred_async_fn);
- } else if (fn->inferred_async_node->type == NodeTypeFnProto) {
- add_error_note(g, msg, fn->inferred_async_node,
- buf_sprintf("async calling convention here"));
- } else if (fn->inferred_async_node->type == NodeTypeSuspend) {
- add_error_note(g, msg, fn->inferred_async_node,
- buf_sprintf("suspends here"));
- } else if (fn->inferred_async_node->type == NodeTypeAwaitExpr) {
- add_error_note(g, msg, fn->inferred_async_node,
- buf_sprintf("await here is a suspend point"));
- } else if (fn->inferred_async_node->type == NodeTypeFnCallExpr &&
- fn->inferred_async_node->data.fn_call_expr.modifier == CallModifierBuiltin)
- {
- add_error_note(g, msg, fn->inferred_async_node,
- buf_sprintf("@frame() causes function to be async"));
- } else {
- add_error_note(g, msg, fn->inferred_async_node,
- buf_sprintf("suspends here"));
- }
-}
-
-// ErrorNone - not async
-// ErrorIsAsync - yes async
-// ErrorSemanticAnalyzeFail - compile error emitted result is invalid
-static Error analyze_callee_async(CodeGen *g, ZigFn *fn, ZigFn *callee, AstNode *call_node,
- bool must_not_be_async, CallModifier modifier)
-{
- if (modifier == CallModifierNoSuspend)
- return ErrorNone;
- bool callee_is_async = false;
- switch (callee->type_entry->data.fn.fn_type_id.cc) {
- case CallingConventionUnspecified:
- break;
- case CallingConventionAsync:
- callee_is_async = true;
- break;
- default:
- return ErrorNone;
- }
- if (!callee_is_async) {
- if (callee->anal_state == FnAnalStateReady) {
- analyze_fn_body(g, callee);
- if (callee->anal_state == FnAnalStateInvalid) {
- return ErrorSemanticAnalyzeFail;
- }
- }
- if (callee->anal_state == FnAnalStateComplete) {
- analyze_fn_async(g, callee, true);
- if (callee->anal_state == FnAnalStateInvalid) {
- if (g->trace_err != nullptr) {
- g->trace_err = add_error_note(g, g->trace_err, call_node,
- buf_sprintf("while checking if '%s' is async", buf_ptr(&fn->symbol_name)));
- }
- return ErrorSemanticAnalyzeFail;
- }
- callee_is_async = fn_is_async(callee);
- } else {
- // If it's already been determined, use that value. Otherwise
- // assume non-async, emit an error later if it turned out to be async.
- if (callee->inferred_async_node == nullptr ||
- callee->inferred_async_node == inferred_async_checking)
- {
- callee->assumed_non_async = call_node;
- callee_is_async = false;
- } else {
- callee_is_async = callee->inferred_async_node != inferred_async_none;
- }
- }
- }
- if (callee_is_async) {
- bool bad_recursion = (fn->inferred_async_node == inferred_async_none);
- fn->inferred_async_node = call_node;
- fn->inferred_async_fn = callee;
- if (must_not_be_async) {
- ErrorMsg *msg = add_node_error(g, fn->proto_node,
- buf_sprintf("function with calling convention '%s' cannot be async",
- calling_convention_name(fn->type_entry->data.fn.fn_type_id.cc)));
- add_async_error_notes(g, msg, fn);
- return ErrorSemanticAnalyzeFail;
- }
- if (bad_recursion) {
- ErrorMsg *msg = add_node_error(g, fn->proto_node,
- buf_sprintf("recursive function cannot be async"));
- add_async_error_notes(g, msg, fn);
- return ErrorSemanticAnalyzeFail;
- }
- if (fn->assumed_non_async != nullptr) {
- ErrorMsg *msg = add_node_error(g, fn->proto_node,
- buf_sprintf("unable to infer whether '%s' should be async",
- buf_ptr(&fn->symbol_name)));
- add_error_note(g, msg, fn->assumed_non_async,
- buf_sprintf("assumed to be non-async here"));
- add_async_error_notes(g, msg, fn);
- fn->anal_state = FnAnalStateInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- return ErrorIsAsync;
- }
- return ErrorNone;
-}
-
-// This function resolves functions being inferred async.
-static void analyze_fn_async(CodeGen *g, ZigFn *fn, bool resolve_frame) {
- if (fn->inferred_async_node == inferred_async_checking) {
- // TODO call graph cycle detected, disallow the recursion
- fn->inferred_async_node = inferred_async_none;
- return;
- }
- if (fn->inferred_async_node == inferred_async_none) {
- return;
- }
- if (fn->inferred_async_node != nullptr) {
- if (resolve_frame) {
- resolve_async_fn_frame(g, fn);
- }
- return;
- }
- fn->inferred_async_node = inferred_async_checking;
-
- bool must_not_be_async = false;
- if (fn->type_entry->data.fn.fn_type_id.cc != CallingConventionUnspecified) {
- must_not_be_async = true;
- fn->inferred_async_node = inferred_async_none;
- }
-
- for (size_t i = 0; i < fn->call_list.length; i += 1) {
- IrInstGenCall *call = fn->call_list.at(i);
- if (call->fn_entry == nullptr) {
- // TODO function pointer call here, could be anything
- continue;
- }
- switch (analyze_callee_async(g, fn, call->fn_entry, call->base.base.source_node, must_not_be_async,
- call->modifier))
- {
- case ErrorSemanticAnalyzeFail:
- fn->anal_state = FnAnalStateInvalid;
- return;
- case ErrorNone:
- continue;
- case ErrorIsAsync:
- if (resolve_frame) {
- resolve_async_fn_frame(g, fn);
- }
- return;
- default:
- zig_unreachable();
- }
- }
- for (size_t i = 0; i < fn->await_list.length; i += 1) {
- IrInstGenAwait *await = fn->await_list.at(i);
- if (await->is_nosuspend) continue;
- switch (analyze_callee_async(g, fn, await->target_fn, await->base.base.source_node, must_not_be_async,
- CallModifierNone))
- {
- case ErrorSemanticAnalyzeFail:
- fn->anal_state = FnAnalStateInvalid;
- return;
- case ErrorNone:
- continue;
- case ErrorIsAsync:
- if (resolve_frame) {
- resolve_async_fn_frame(g, fn);
- }
- return;
- default:
- zig_unreachable();
- }
- }
- fn->inferred_async_node = inferred_async_none;
-}
-
-static void analyze_fn_ir(CodeGen *g, ZigFn *fn, AstNode *return_type_node) {
- ZigType *fn_type = fn->type_entry;
- assert(!fn_type->data.fn.is_generic);
- FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
-
- if (fn->analyzed_executable.begin_scope == nullptr) {
- fn->analyzed_executable.begin_scope = &fn->def_scope->base;
- }
- if (fn->analyzed_executable.source_node == nullptr) {
- fn->analyzed_executable.source_node = fn->body_node;
- }
- ZigType *block_return_type = ir_analyze(g, fn->ir_executable,
- &fn->analyzed_executable, fn_type_id->return_type, return_type_node, nullptr);
- fn->src_implicit_return_type = block_return_type;
-
- if (type_is_invalid(block_return_type) || fn->analyzed_executable.first_err_trace_msg != nullptr) {
- assert(g->errors.length > 0);
- fn->anal_state = FnAnalStateInvalid;
- return;
- }
-
- if (fn_type_id->return_type->id == ZigTypeIdErrorUnion) {
- ZigType *return_err_set_type = fn_type_id->return_type->data.error_union.err_set_type;
- if (return_err_set_type->data.error_set.infer_fn != nullptr &&
- return_err_set_type->data.error_set.incomplete)
- {
- // The inferred error set type is null if the function doesn't
- // return any error
- ZigType *inferred_err_set_type = nullptr;
-
- if (fn->src_implicit_return_type->id == ZigTypeIdErrorSet) {
- inferred_err_set_type = fn->src_implicit_return_type;
- } else if (fn->src_implicit_return_type->id == ZigTypeIdErrorUnion) {
- inferred_err_set_type = fn->src_implicit_return_type->data.error_union.err_set_type;
- }
-
- if (inferred_err_set_type != nullptr) {
- if (inferred_err_set_type->data.error_set.infer_fn != nullptr &&
- inferred_err_set_type->data.error_set.incomplete)
- {
- if (!resolve_inferred_error_set(g, inferred_err_set_type, return_type_node)) {
- fn->anal_state = FnAnalStateInvalid;
- return;
- }
- }
-
- return_err_set_type->data.error_set.incomplete = false;
- if (type_is_global_error_set(inferred_err_set_type)) {
- return_err_set_type->data.error_set.err_count = UINT32_MAX;
- } else {
- return_err_set_type->data.error_set.err_count = inferred_err_set_type->data.error_set.err_count;
- if (inferred_err_set_type->data.error_set.err_count > 0) {
- return_err_set_type->data.error_set.errors = heap::c_allocator.allocate(inferred_err_set_type->data.error_set.err_count);
- for (uint32_t i = 0; i < inferred_err_set_type->data.error_set.err_count; i += 1) {
- return_err_set_type->data.error_set.errors[i] = inferred_err_set_type->data.error_set.errors[i];
- }
- }
- }
- } else {
- return_err_set_type->data.error_set.incomplete = false;
- return_err_set_type->data.error_set.err_count = 0;
- }
- }
- }
-
- CallingConvention cc = fn->type_entry->data.fn.fn_type_id.cc;
- if (cc != CallingConventionUnspecified && cc != CallingConventionAsync &&
- fn->inferred_async_node != nullptr &&
- fn->inferred_async_node != inferred_async_checking &&
- fn->inferred_async_node != inferred_async_none)
- {
- ErrorMsg *msg = add_node_error(g, fn->proto_node,
- buf_sprintf("function with calling convention '%s' cannot be async",
- calling_convention_name(cc)));
- add_async_error_notes(g, msg, fn);
- fn->anal_state = FnAnalStateInvalid;
- }
-
- if (g->verbose_ir) {
- fprintf(stderr, "fn %s() { // (analyzed)\n", buf_ptr(&fn->symbol_name));
- ir_print_gen(g, stderr, &fn->analyzed_executable, 4);
- fprintf(stderr, "}\n");
- }
- fn->anal_state = FnAnalStateComplete;
-}
-
-static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry) {
- assert(fn_table_entry->anal_state != FnAnalStateProbing);
- if (fn_table_entry->anal_state != FnAnalStateReady)
- return;
-
- fn_table_entry->anal_state = FnAnalStateProbing;
- update_progress_display(g);
-
- AstNode *return_type_node = (fn_table_entry->proto_node != nullptr) ?
- fn_table_entry->proto_node->data.fn_proto.return_type : fn_table_entry->fndef_scope->base.source_node;
-
- assert(fn_table_entry->fndef_scope);
- if (!fn_table_entry->child_scope)
- fn_table_entry->child_scope = &fn_table_entry->fndef_scope->base;
-
- if (define_local_param_variables(g, fn_table_entry) != ErrorNone) {
- fn_table_entry->anal_state = FnAnalStateInvalid;
- return;
- }
-
- ZigType *fn_type = fn_table_entry->type_entry;
- assert(!fn_type->data.fn.is_generic);
-
- if (!ir_gen_fn(g, fn_table_entry)) {
- fn_table_entry->anal_state = FnAnalStateInvalid;
- return;
- }
-
- if (fn_table_entry->ir_executable->first_err_trace_msg != nullptr) {
- fn_table_entry->anal_state = FnAnalStateInvalid;
- return;
- }
-
- if (g->verbose_ir) {
- fprintf(stderr, "\n");
- ast_render(stderr, fn_table_entry->body_node, 4);
- fprintf(stderr, "\nfn %s() { // (IR)\n", buf_ptr(&fn_table_entry->symbol_name));
- ir_print_src(g, stderr, fn_table_entry->ir_executable, 4);
- fprintf(stderr, "}\n");
- }
-
- analyze_fn_ir(g, fn_table_entry, return_type_node);
-}
-
-ZigType *add_source_file(CodeGen *g, ZigPackage *package, Buf *resolved_path, Buf *source_code,
- SourceKind source_kind)
-{
- if (g->verbose_tokenize) {
- fprintf(stderr, "\nOriginal Source (%s):\n", buf_ptr(resolved_path));
- fprintf(stderr, "----------------\n");
- fprintf(stderr, "%s\n", buf_ptr(source_code));
-
- fprintf(stderr, "\nTokens:\n");
- fprintf(stderr, "---------\n");
- }
-
- Tokenization tokenization = {0};
- tokenize(source_code, &tokenization);
-
- if (tokenization.err) {
- ErrorMsg *err = err_msg_create_with_line(resolved_path, tokenization.err_line, tokenization.err_column,
- source_code, tokenization.line_offsets, tokenization.err);
-
- print_err_msg(err, g->err_color);
- exit(1);
- }
-
- if (g->verbose_tokenize) {
- print_tokens(source_code, tokenization.tokens);
-
- fprintf(stderr, "\nAST:\n");
- fprintf(stderr, "------\n");
- }
-
- Buf *src_dirname = buf_alloc();
- Buf *src_basename = buf_alloc();
- os_path_split(resolved_path, src_dirname, src_basename);
-
- Buf noextname = BUF_INIT;
- os_path_extname(resolved_path, &noextname, nullptr);
-
- Buf *pkg_root_src_dir = &package->root_src_dir;
- Buf resolved_root_src_dir = os_path_resolve(&pkg_root_src_dir, 1);
-
- Buf *namespace_name = buf_create_from_buf(&package->pkg_path);
- if (source_kind == SourceKindNonRoot) {
- assert(buf_starts_with_buf(resolved_path, &resolved_root_src_dir));
- if (buf_len(namespace_name) != 0) {
- buf_append_char(namespace_name, NAMESPACE_SEP_CHAR);
- }
- // The namespace components are obtained from the relative path to the
- // source directory
- if (buf_len(&noextname) > buf_len(&resolved_root_src_dir)) {
- // Skip the trailing separator
- buf_append_mem(namespace_name,
- buf_ptr(&noextname) + buf_len(&resolved_root_src_dir) + 1,
- buf_len(&noextname) - buf_len(&resolved_root_src_dir) - 1);
- }
- buf_replace(namespace_name, ZIG_OS_SEP_CHAR, NAMESPACE_SEP_CHAR);
- }
- Buf *bare_name = buf_alloc();
- os_path_extname(src_basename, bare_name, nullptr);
-
- RootStruct *root_struct = heap::c_allocator.create();
- root_struct->package = package;
- root_struct->source_code = source_code;
- root_struct->line_offsets = tokenization.line_offsets;
- root_struct->path = resolved_path;
- root_struct->di_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname));
- ZigType *import_entry = get_root_container_type(g, buf_ptr(namespace_name), bare_name, root_struct);
- if (source_kind == SourceKindRoot) {
- assert(g->root_import == nullptr);
- g->root_import = import_entry;
- }
- g->import_table.put(resolved_path, import_entry);
-
- AstNode *root_node = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color);
- assert(root_node != nullptr);
- assert(root_node->type == NodeTypeContainerDecl);
- import_entry->data.structure.decl_node = root_node;
- import_entry->data.structure.decls_scope->base.source_node = root_node;
- if (g->verbose_ast) {
- ast_print(stderr, root_node, 0);
- }
-
- for (size_t decl_i = 0; decl_i < root_node->data.container_decl.decls.length; decl_i += 1) {
- AstNode *top_level_decl = root_node->data.container_decl.decls.at(decl_i);
- scan_decls(g, import_entry->data.structure.decls_scope, top_level_decl);
- }
-
- TldContainer *tld_container = heap::c_allocator.create();
- init_tld(&tld_container->base, TldIdContainer, namespace_name, VisibModPub, root_node, nullptr);
- tld_container->type_entry = import_entry;
- tld_container->decls_scope = import_entry->data.structure.decls_scope;
- g->resolve_queue.append(&tld_container->base);
-
- return import_entry;
-}
-
-void semantic_analyze(CodeGen *g) {
- while (g->resolve_queue_index < g->resolve_queue.length ||
- g->fn_defs_index < g->fn_defs.length)
- {
- for (; g->resolve_queue_index < g->resolve_queue.length; g->resolve_queue_index += 1) {
- Tld *tld = g->resolve_queue.at(g->resolve_queue_index);
- g->trace_err = nullptr;
- AstNode *source_node = nullptr;
- resolve_top_level_decl(g, tld, source_node, false);
- }
-
- for (; g->fn_defs_index < g->fn_defs.length; g->fn_defs_index += 1) {
- ZigFn *fn_entry = g->fn_defs.at(g->fn_defs_index);
- g->trace_err = nullptr;
- analyze_fn_body(g, fn_entry);
- }
- }
-
- if (g->errors.length != 0) {
- return;
- }
-
- // second pass over functions for detecting async
- for (g->fn_defs_index = 0; g->fn_defs_index < g->fn_defs.length; g->fn_defs_index += 1) {
- ZigFn *fn = g->fn_defs.at(g->fn_defs_index);
- g->trace_err = nullptr;
- analyze_fn_async(g, fn, true);
- if (fn->anal_state == FnAnalStateInvalid)
- continue;
- if (fn_is_async(fn) && fn->non_async_node != nullptr) {
- ErrorMsg *msg = add_node_error(g, fn->proto_node,
- buf_sprintf("'%s' cannot be async", buf_ptr(&fn->symbol_name)));
- add_error_note(g, msg, fn->non_async_node,
- buf_sprintf("required to be non-async here"));
- add_async_error_notes(g, msg, fn);
- }
- }
-}
-
-ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) {
- assert(size_in_bits <= 65535);
- TypeId type_id = {};
- type_id.id = ZigTypeIdInt;
- type_id.data.integer.is_signed = is_signed;
- type_id.data.integer.bit_count = size_in_bits;
-
- {
- auto entry = g->type_table.maybe_get(type_id);
- if (entry)
- return entry->value;
- }
-
- ZigType *new_entry = make_int_type(g, is_signed, size_in_bits);
- g->type_table.put(type_id, new_entry);
- return new_entry;
-}
-
-Error is_valid_vector_elem_type(CodeGen *g, ZigType *elem_type, bool *result) {
- if (elem_type->id == ZigTypeIdInt ||
- elem_type->id == ZigTypeIdFloat ||
- elem_type->id == ZigTypeIdBool)
- {
- *result = true;
- return ErrorNone;
- }
-
- Error err;
- ZigType *ptr_type;
- if ((err = get_codegen_ptr_type(g, elem_type, &ptr_type))) return err;
- if (ptr_type != nullptr) {
- *result = true;
- return ErrorNone;
- }
-
- *result = false;
- return ErrorNone;
-}
-
-ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type) {
- Error err;
-
- bool valid_vector_elem;
- if ((err = is_valid_vector_elem_type(g, elem_type, &valid_vector_elem))) {
- codegen_report_errors_and_exit(g);
- }
- assert(valid_vector_elem);
-
- TypeId type_id = {};
- type_id.id = ZigTypeIdVector;
- type_id.data.vector.len = len;
- type_id.data.vector.elem_type = elem_type;
-
- {
- auto entry = g->type_table.maybe_get(type_id);
- if (entry)
- return entry->value;
- }
-
- ZigType *entry = new_type_table_entry(ZigTypeIdVector);
- if ((len != 0) && type_has_bits(g, elem_type)) {
- // Vectors can only be ints, floats, bools, or pointers. ints (inc. bools) and floats have trivially resolvable
- // llvm type refs. pointers we will use usize instead.
- LLVMTypeRef example_vector_llvm_type;
- if (elem_type->id == ZigTypeIdPointer) {
- example_vector_llvm_type = LLVMVectorType(g->builtin_types.entry_usize->llvm_type, len);
- } else {
- example_vector_llvm_type = LLVMVectorType(elem_type->llvm_type, len);
- }
- assert(example_vector_llvm_type != nullptr);
- entry->size_in_bits = elem_type->size_in_bits * len;
- entry->abi_size = LLVMABISizeOfType(g->target_data_ref, example_vector_llvm_type);
- entry->abi_align = LLVMABIAlignmentOfType(g->target_data_ref, example_vector_llvm_type);
- }
- entry->data.vector.len = len;
- entry->data.vector.elem_type = elem_type;
- entry->data.vector.padding = 0;
-
- buf_resize(&entry->name, 0);
- buf_appendf(&entry->name, "@Vector(%u, %s)", len, buf_ptr(&elem_type->name));
-
- g->type_table.put(type_id, entry);
- return entry;
-}
-
-ZigType **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type) {
- return &g->builtin_types.entry_c_int[c_int_type];
-}
-
-ZigType *get_c_int_type(CodeGen *g, CIntType c_int_type) {
- return *get_c_int_type_ptr(g, c_int_type);
-}
-
-bool handle_is_ptr(CodeGen *g, ZigType *type_entry) {
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- case ZigTypeIdMetaType:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdBoundFn:
- case ZigTypeIdOpaque:
- zig_unreachable();
- case ZigTypeIdUnreachable:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdInt:
- case ZigTypeIdFloat:
- case ZigTypeIdPointer:
- case ZigTypeIdErrorSet:
- case ZigTypeIdFn:
- case ZigTypeIdEnum:
- case ZigTypeIdVector:
- case ZigTypeIdAnyFrame:
- return false;
- case ZigTypeIdArray:
- case ZigTypeIdStruct:
- case ZigTypeIdFnFrame:
- return type_has_bits(g, type_entry);
- case ZigTypeIdErrorUnion:
- return type_has_bits(g, type_entry->data.error_union.payload_type);
- case ZigTypeIdOptional:
- return type_has_bits(g, type_entry->data.maybe.child_type) &&
- !type_is_nonnull_ptr(g, type_entry->data.maybe.child_type) &&
- type_entry->data.maybe.child_type->id != ZigTypeIdErrorSet;
- case ZigTypeIdUnion:
- return type_has_bits(g, type_entry) && type_entry->data.unionation.gen_field_count != 0;
-
- }
- zig_unreachable();
-}
-
-static uint32_t hash_ptr(void *ptr) {
- return (uint32_t)(((uintptr_t)ptr) % UINT32_MAX);
-}
-
-static uint32_t hash_size(size_t x) {
- return (uint32_t)(x % UINT32_MAX);
-}
-
-uint32_t fn_table_entry_hash(ZigFn* value) {
- return ptr_hash(value);
-}
-
-bool fn_table_entry_eql(ZigFn *a, ZigFn *b) {
- return ptr_eq(a, b);
-}
-
-uint32_t fn_type_id_hash(FnTypeId *id) {
- uint32_t result = 0;
- result += ((uint32_t)(id->cc)) * (uint32_t)3349388391;
- result += id->is_var_args ? (uint32_t)1931444534 : 0;
- result += hash_ptr(id->return_type);
- result += id->alignment * 0xd3b3f3e2;
- for (size_t i = 0; i < id->param_count; i += 1) {
- FnTypeParamInfo *info = &id->param_info[i];
- result += info->is_noalias ? (uint32_t)892356923 : 0;
- result += hash_ptr(info->type);
- }
- return result;
-}
-
-bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) {
- if (a->cc != b->cc ||
- a->return_type != b->return_type ||
- a->is_var_args != b->is_var_args ||
- a->param_count != b->param_count ||
- a->alignment != b->alignment)
- {
- return false;
- }
- for (size_t i = 0; i < a->param_count; i += 1) {
- FnTypeParamInfo *a_param_info = &a->param_info[i];
- FnTypeParamInfo *b_param_info = &b->param_info[i];
-
- if (a_param_info->type != b_param_info->type ||
- a_param_info->is_noalias != b_param_info->is_noalias)
- {
- return false;
- }
- }
- return true;
-}
-
-static uint32_t hash_const_val_error_set(ZigValue *const_val) {
- assert(const_val->data.x_err_set != nullptr);
- return const_val->data.x_err_set->value ^ 2630160122;
-}
-
-static uint32_t hash_const_val_ptr(ZigValue *const_val) {
- uint32_t hash_val = 0;
- switch (const_val->data.x_ptr.mut) {
- case ConstPtrMutRuntimeVar:
- hash_val += (uint32_t)3500721036;
- break;
- case ConstPtrMutComptimeConst:
- hash_val += (uint32_t)4214318515;
- break;
- case ConstPtrMutInfer:
- case ConstPtrMutComptimeVar:
- hash_val += (uint32_t)1103195694;
- break;
- }
- switch (const_val->data.x_ptr.special) {
- case ConstPtrSpecialInvalid:
- zig_unreachable();
- case ConstPtrSpecialRef:
- hash_val += (uint32_t)2478261866;
- hash_val += hash_ptr(const_val->data.x_ptr.data.ref.pointee);
- return hash_val;
- case ConstPtrSpecialBaseArray:
- hash_val += (uint32_t)1764906839;
- hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val);
- hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index);
- return hash_val;
- case ConstPtrSpecialSubArray:
- hash_val += (uint32_t)2643358777;
- hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val);
- hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index);
- return hash_val;
- case ConstPtrSpecialBaseStruct:
- hash_val += (uint32_t)3518317043;
- hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val);
- hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index);
- return hash_val;
- case ConstPtrSpecialBaseErrorUnionCode:
- hash_val += (uint32_t)2994743799;
- hash_val += hash_ptr(const_val->data.x_ptr.data.base_err_union_code.err_union_val);
- return hash_val;
- case ConstPtrSpecialBaseErrorUnionPayload:
- hash_val += (uint32_t)3456080131;
- hash_val += hash_ptr(const_val->data.x_ptr.data.base_err_union_payload.err_union_val);
- return hash_val;
- case ConstPtrSpecialBaseOptionalPayload:
- hash_val += (uint32_t)3163140517;
- hash_val += hash_ptr(const_val->data.x_ptr.data.base_optional_payload.optional_val);
- return hash_val;
- case ConstPtrSpecialHardCodedAddr:
- hash_val += (uint32_t)4048518294;
- hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr);
- return hash_val;
- case ConstPtrSpecialDiscard:
- hash_val += 2010123162;
- return hash_val;
- case ConstPtrSpecialFunction:
- hash_val += (uint32_t)2590901619;
- hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry);
- return hash_val;
- case ConstPtrSpecialNull:
- hash_val += (uint32_t)1486246455;
- return hash_val;
- }
- zig_unreachable();
-}
-
-static uint32_t hash_const_val(ZigValue *const_val) {
- assert(const_val->special == ConstValSpecialStatic);
- switch (const_val->type->id) {
- case ZigTypeIdOpaque:
- zig_unreachable();
- case ZigTypeIdBool:
- return const_val->data.x_bool ? (uint32_t)127863866 : (uint32_t)215080464;
- case ZigTypeIdMetaType:
- return hash_ptr(const_val->data.x_type);
- case ZigTypeIdVoid:
- return (uint32_t)4149439618;
- case ZigTypeIdInt:
- case ZigTypeIdComptimeInt:
- {
- uint32_t result = 1331471175;
- for (size_t i = 0; i < const_val->data.x_bigint.digit_count; i += 1) {
- uint64_t digit = bigint_ptr(&const_val->data.x_bigint)[i];
- result ^= ((uint32_t)(digit >> 32)) ^ (uint32_t)(result);
- }
- return result;
- }
- case ZigTypeIdEnumLiteral:
- return buf_hash(const_val->data.x_enum_literal) * (uint32_t)2691276464;
- case ZigTypeIdEnum:
- {
- uint32_t result = 31643936;
- for (size_t i = 0; i < const_val->data.x_enum_tag.digit_count; i += 1) {
- uint64_t digit = bigint_ptr(&const_val->data.x_enum_tag)[i];
- result ^= ((uint32_t)(digit >> 32)) ^ (uint32_t)(result);
- }
- return result;
- }
- case ZigTypeIdFloat:
- switch (const_val->type->data.floating.bit_count) {
- case 16:
- {
- uint16_t result;
- static_assert(sizeof(result) == sizeof(const_val->data.x_f16), "");
- memcpy(&result, &const_val->data.x_f16, sizeof(result));
- return result * 65537u;
- }
- case 32:
- {
- uint32_t result;
- memcpy(&result, &const_val->data.x_f32, 4);
- return result ^ 4084870010;
- }
- case 64:
- {
- uint32_t ints[2];
- memcpy(&ints[0], &const_val->data.x_f64, 8);
- return ints[0] ^ ints[1] ^ 0x22ed43c6;
- }
- case 128:
- {
- uint32_t ints[4];
- memcpy(&ints[0], &const_val->data.x_f128, 16);
- return ints[0] ^ ints[1] ^ ints[2] ^ ints[3] ^ 0xb5ffef27;
- }
- default:
- zig_unreachable();
- }
- case ZigTypeIdComptimeFloat:
- {
- float128_t f128 = bigfloat_to_f128(&const_val->data.x_bigfloat);
- uint32_t ints[4];
- memcpy(&ints[0], &f128, 16);
- return ints[0] ^ ints[1] ^ ints[2] ^ ints[3] ^ 0xed8b3dfb;
- }
- case ZigTypeIdFn:
- assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst);
- assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction);
- return 3677364617 ^ hash_ptr(const_val->data.x_ptr.data.fn.fn_entry);
- case ZigTypeIdPointer:
- return hash_const_val_ptr(const_val);
- case ZigTypeIdUndefined:
- return 162837799;
- case ZigTypeIdNull:
- return 844854567;
- case ZigTypeIdArray:
- // TODO better hashing algorithm
- return 1166190605;
- case ZigTypeIdStruct:
- // TODO better hashing algorithm
- return 1532530855;
- case ZigTypeIdUnion:
- // TODO better hashing algorithm
- return 2709806591;
- case ZigTypeIdOptional:
- if (get_src_ptr_type(const_val->type) != nullptr) {
- return hash_const_val_ptr(const_val) * (uint32_t)1992916303;
- } else if (const_val->type->data.maybe.child_type->id == ZigTypeIdErrorSet) {
- return hash_const_val_error_set(const_val) * (uint32_t)3147031929;
- } else {
- if (const_val->data.x_optional) {
- return hash_const_val(const_val->data.x_optional) * (uint32_t)1992916303;
- } else {
- return 4016830364;
- }
- }
- case ZigTypeIdErrorUnion:
- // TODO better hashing algorithm
- return 3415065496;
- case ZigTypeIdErrorSet:
- return hash_const_val_error_set(const_val);
- case ZigTypeIdVector:
- // TODO better hashing algorithm
- return 3647867726;
- case ZigTypeIdFnFrame:
- // TODO better hashing algorithm
- return 675741936;
- case ZigTypeIdAnyFrame:
- // TODO better hashing algorithm
- return 3747294894;
- case ZigTypeIdBoundFn: {
- assert(const_val->data.x_bound_fn.fn != nullptr);
- return 3677364617 ^ hash_ptr(const_val->data.x_bound_fn.fn);
- }
- case ZigTypeIdInvalid:
- case ZigTypeIdUnreachable:
- zig_unreachable();
- }
- zig_unreachable();
-}
-
-uint32_t generic_fn_type_id_hash(GenericFnTypeId *id) {
- uint32_t result = 0;
- result += hash_ptr(id->fn_entry);
- for (size_t i = 0; i < id->param_count; i += 1) {
- ZigValue *generic_param = &id->params[i];
- if (generic_param->special != ConstValSpecialRuntime) {
- result += hash_const_val(generic_param);
- result += hash_ptr(generic_param->type);
- }
- }
- return result;
-}
-
-bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b) {
- assert(a->fn_entry);
- if (a->fn_entry != b->fn_entry) return false;
- if (a->param_count != b->param_count) return false;
- for (size_t i = 0; i < a->param_count; i += 1) {
- ZigValue *a_val = &a->params[i];
- ZigValue *b_val = &b->params[i];
- if (a_val->type != b_val->type) return false;
- if (a_val->special != ConstValSpecialRuntime && b_val->special != ConstValSpecialRuntime) {
- assert(a_val->special == ConstValSpecialStatic);
- assert(b_val->special == ConstValSpecialStatic);
- if (!const_values_equal(a->codegen, a_val, b_val)) {
- return false;
- }
- } else {
- assert(a_val->special == ConstValSpecialRuntime && b_val->special == ConstValSpecialRuntime);
- }
- }
- return true;
-}
-
-static bool can_mutate_comptime_var_state(ZigValue *value) {
- assert(value != nullptr);
- if (value->special == ConstValSpecialUndef)
- return false;
- switch (value->type->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdMetaType:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdUnreachable:
- case ZigTypeIdInt:
- case ZigTypeIdVector:
- case ZigTypeIdFloat:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdBoundFn:
- case ZigTypeIdFn:
- case ZigTypeIdOpaque:
- case ZigTypeIdErrorSet:
- case ZigTypeIdEnum:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- return false;
-
- case ZigTypeIdPointer:
- return value->data.x_ptr.mut == ConstPtrMutComptimeVar;
-
- case ZigTypeIdArray:
- if (value->special == ConstValSpecialUndef)
- return false;
- if (value->type->data.array.len == 0)
- return false;
- switch (value->data.x_array.special) {
- case ConstArraySpecialUndef:
- case ConstArraySpecialBuf:
- return false;
- case ConstArraySpecialNone:
- for (uint32_t i = 0; i < value->type->data.array.len; i += 1) {
- if (can_mutate_comptime_var_state(&value->data.x_array.data.s_none.elements[i]))
- return true;
- }
- return false;
- }
- zig_unreachable();
- case ZigTypeIdStruct:
- for (uint32_t i = 0; i < value->type->data.structure.src_field_count; i += 1) {
- if (can_mutate_comptime_var_state(value->data.x_struct.fields[i]))
- return true;
- }
- return false;
-
- case ZigTypeIdOptional:
- if (get_src_ptr_type(value->type) != nullptr)
- return value->data.x_ptr.mut == ConstPtrMutComptimeVar;
- if (value->data.x_optional == nullptr)
- return false;
- return can_mutate_comptime_var_state(value->data.x_optional);
-
- case ZigTypeIdErrorUnion:
- if (value->data.x_err_union.error_set->data.x_err_set != nullptr)
- return false;
- assert(value->data.x_err_union.payload != nullptr);
- return can_mutate_comptime_var_state(value->data.x_err_union.payload);
-
- case ZigTypeIdUnion:
- return can_mutate_comptime_var_state(value->data.x_union.payload);
- }
- zig_unreachable();
-}
-
-static bool return_type_is_cacheable(ZigType *return_type) {
- switch (return_type->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdMetaType:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdUnreachable:
- case ZigTypeIdInt:
- case ZigTypeIdFloat:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdBoundFn:
- case ZigTypeIdFn:
- case ZigTypeIdOpaque:
- case ZigTypeIdErrorSet:
- case ZigTypeIdEnum:
- case ZigTypeIdPointer:
- case ZigTypeIdVector:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- return true;
-
- case ZigTypeIdArray:
- case ZigTypeIdStruct:
- case ZigTypeIdUnion:
- return false;
-
- case ZigTypeIdOptional:
- return return_type_is_cacheable(return_type->data.maybe.child_type);
-
- case ZigTypeIdErrorUnion:
- return return_type_is_cacheable(return_type->data.error_union.payload_type);
- }
- zig_unreachable();
-}
-
-bool fn_eval_cacheable(Scope *scope, ZigType *return_type) {
- if (!return_type_is_cacheable(return_type))
- return false;
- while (scope) {
- if (scope->id == ScopeIdVarDecl) {
- ScopeVarDecl *var_scope = (ScopeVarDecl *)scope;
- if (type_is_invalid(var_scope->var->var_type))
- return false;
- if (var_scope->var->const_value->special == ConstValSpecialUndef)
- return false;
- if (can_mutate_comptime_var_state(var_scope->var->const_value))
- return false;
- } else if (scope->id == ScopeIdFnDef) {
- return true;
- } else {
- zig_unreachable();
- }
-
- scope = scope->parent;
- }
- zig_unreachable();
-}
-
-uint32_t fn_eval_hash(Scope* scope) {
- uint32_t result = 0;
- while (scope) {
- if (scope->id == ScopeIdVarDecl) {
- ScopeVarDecl *var_scope = (ScopeVarDecl *)scope;
- result += hash_const_val(var_scope->var->const_value);
- } else if (scope->id == ScopeIdFnDef) {
- ScopeFnDef *fn_scope = (ScopeFnDef *)scope;
- result += hash_ptr(fn_scope->fn_entry);
- return result;
- } else {
- zig_unreachable();
- }
-
- scope = scope->parent;
- }
- zig_unreachable();
-}
-
-bool fn_eval_eql(Scope *a, Scope *b) {
- assert(a->codegen != nullptr);
- assert(b->codegen != nullptr);
- while (a && b) {
- if (a->id != b->id)
- return false;
-
- if (a->id == ScopeIdVarDecl) {
- ScopeVarDecl *a_var_scope = (ScopeVarDecl *)a;
- ScopeVarDecl *b_var_scope = (ScopeVarDecl *)b;
- if (a_var_scope->var->var_type != b_var_scope->var->var_type)
- return false;
- if (a_var_scope->var->var_type == a_var_scope->var->const_value->type &&
- b_var_scope->var->var_type == b_var_scope->var->const_value->type)
- {
- if (!const_values_equal(a->codegen, a_var_scope->var->const_value, b_var_scope->var->const_value))
- return false;
- } else {
- zig_panic("TODO comptime ptr reinterpret for fn_eval_eql");
- }
- } else if (a->id == ScopeIdFnDef) {
- ScopeFnDef *a_fn_scope = (ScopeFnDef *)a;
- ScopeFnDef *b_fn_scope = (ScopeFnDef *)b;
- if (a_fn_scope->fn_entry != b_fn_scope->fn_entry)
- return false;
-
- return true;
- } else {
- zig_unreachable();
- }
-
- a = a->parent;
- b = b->parent;
- }
- return false;
-}
-
-// Deprecated. Use type_has_bits2.
-bool type_has_bits(CodeGen *g, ZigType *type_entry) {
- Error err;
- bool result;
- if ((err = type_has_bits2(g, type_entry, &result))) {
- codegen_report_errors_and_exit(g);
- }
- return result;
-}
-
-// Whether the type has bits at runtime.
-Error type_has_bits2(CodeGen *g, ZigType *type_entry, bool *result) {
- Error err;
-
- if (type_is_invalid(type_entry))
- return ErrorSemanticAnalyzeFail;
-
- if (type_entry->id == ZigTypeIdStruct &&
- type_entry->data.structure.resolve_status == ResolveStatusBeingInferred)
- {
- *result = true;
- return ErrorNone;
- }
-
- if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown)))
- return err;
-
- *result = type_entry->abi_size != 0;
- return ErrorNone;
-}
-
-// Whether you can infer the value based solely on the type.
-OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) {
- assert(type_entry != nullptr);
-
- if (type_entry->one_possible_value != OnePossibleValueInvalid)
- return type_entry->one_possible_value;
-
- if (type_entry->id == ZigTypeIdStruct &&
- type_entry->data.structure.resolve_status == ResolveStatusBeingInferred)
- {
- return OnePossibleValueNo;
- }
-
- Error err;
- if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown)))
- return OnePossibleValueInvalid;
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdOpaque:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdMetaType:
- case ZigTypeIdBoundFn:
- case ZigTypeIdOptional:
- case ZigTypeIdFn:
- case ZigTypeIdBool:
- case ZigTypeIdFloat:
- case ZigTypeIdErrorUnion:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- return OnePossibleValueNo;
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdVoid:
- case ZigTypeIdUnreachable:
- return OnePossibleValueYes;
- case ZigTypeIdArray:
- if (type_entry->data.array.len == 0)
- return OnePossibleValueYes;
- return type_has_one_possible_value(g, type_entry->data.array.child_type);
- case ZigTypeIdStruct:
- // If the recursive function call asks, then we are not one possible value.
- type_entry->one_possible_value = OnePossibleValueNo;
- for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
- TypeStructField *field = type_entry->data.structure.fields[i];
- if (field->is_comptime) {
- // If this field is comptime then the field can only be one possible value
- continue;
- }
- OnePossibleValue opv = (field->type_entry != nullptr) ?
- type_has_one_possible_value(g, field->type_entry) :
- type_val_resolve_has_one_possible_value(g, field->type_val);
- switch (opv) {
- case OnePossibleValueInvalid:
- type_entry->one_possible_value = OnePossibleValueInvalid;
- return OnePossibleValueInvalid;
- case OnePossibleValueNo:
- return OnePossibleValueNo;
- case OnePossibleValueYes:
- continue;
- }
- }
- type_entry->one_possible_value = OnePossibleValueYes;
- return OnePossibleValueYes;
- case ZigTypeIdErrorSet:
- case ZigTypeIdEnum:
- case ZigTypeIdInt:
- case ZigTypeIdVector:
- return type_has_bits(g, type_entry) ? OnePossibleValueNo : OnePossibleValueYes;
- case ZigTypeIdPointer: {
- ZigType *elem_type = type_entry->data.pointer.child_type;
- // If the recursive function call asks, then we are not one possible value.
- type_entry->one_possible_value = OnePossibleValueNo;
- // Now update it to be the value of the recursive call.
- type_entry->one_possible_value = type_has_one_possible_value(g, elem_type);
- return type_entry->one_possible_value;
- }
- case ZigTypeIdUnion:
- if (type_entry->data.unionation.src_field_count > 1)
- return OnePossibleValueNo;
- TypeUnionField *only_field = &type_entry->data.unionation.fields[0];
- if (only_field->type_entry != nullptr) {
- return type_has_one_possible_value(g, only_field->type_entry);
- }
- return type_val_resolve_has_one_possible_value(g, only_field->type_val);
- }
- zig_unreachable();
-}
-
-ZigValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry) {
- auto entry = g->one_possible_values.maybe_get(type_entry);
- if (entry != nullptr) {
- return entry->value;
- }
- ZigValue *result = g->pass1_arena->create();
- result->type = type_entry;
- result->special = ConstValSpecialStatic;
-
- if (result->type->id == ZigTypeIdStruct) {
- // The fields array cannot be left unpopulated
- const ZigType *struct_type = result->type;
- const size_t field_count = struct_type->data.structure.src_field_count;
- result->data.x_struct.fields = alloc_const_vals_ptrs(g, field_count);
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *field = struct_type->data.structure.fields[i];
- if (field->is_comptime) {
- copy_const_val(g, result->data.x_struct.fields[i], field->init_val);
- continue;
- }
- ZigType *field_type = resolve_struct_field_type(g, field);
- assert(field_type != nullptr);
- result->data.x_struct.fields[i] = get_the_one_possible_value(g, field_type);
- }
- } else if (result->type->id == ZigTypeIdArray) {
- // The elements array cannot be left unpopulated
- ZigType *array_type = result->type;
- ZigType *elem_type = array_type->data.array.child_type;
- const size_t elem_count = array_type->data.array.len;
-
- result->data.x_array.data.s_none.elements = g->pass1_arena->allocate(elem_count);
- for (size_t i = 0; i < elem_count; i += 1) {
- ZigValue *elem_val = &result->data.x_array.data.s_none.elements[i];
- copy_const_val(g, elem_val, get_the_one_possible_value(g, elem_type));
- }
- } else if (result->type->id == ZigTypeIdPointer) {
- result->data.x_ptr.special = ConstPtrSpecialRef;
- result->data.x_ptr.data.ref.pointee = get_the_one_possible_value(g, result->type->data.pointer.child_type);
- }
- g->one_possible_values.put(type_entry, result);
- return result;
-}
-
-ReqCompTime type_requires_comptime(CodeGen *g, ZigType *ty) {
- Error err;
- if (ty == g->builtin_types.entry_anytype) {
- return ReqCompTimeYes;
- }
- switch (ty->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdMetaType:
- case ZigTypeIdBoundFn:
- return ReqCompTimeYes;
- case ZigTypeIdArray:
- return type_requires_comptime(g, ty->data.array.child_type);
- case ZigTypeIdStruct:
- if (ty->data.structure.resolve_loop_flag_zero_bits) {
- // Does a struct which contains a pointer field to itself require comptime? No.
- return ReqCompTimeNo;
- }
- if ((err = type_resolve(g, ty, ResolveStatusZeroBitsKnown)))
- return ReqCompTimeInvalid;
- return ty->data.structure.requires_comptime ? ReqCompTimeYes : ReqCompTimeNo;
- case ZigTypeIdUnion:
- if (ty->data.unionation.resolve_loop_flag_zero_bits) {
- // Does a union which contains a pointer field to itself require comptime? No.
- return ReqCompTimeNo;
- }
- if ((err = type_resolve(g, ty, ResolveStatusZeroBitsKnown)))
- return ReqCompTimeInvalid;
- return ty->data.unionation.requires_comptime ? ReqCompTimeYes : ReqCompTimeNo;
- case ZigTypeIdOptional:
- return type_requires_comptime(g, ty->data.maybe.child_type);
- case ZigTypeIdErrorUnion:
- return type_requires_comptime(g, ty->data.error_union.payload_type);
- case ZigTypeIdPointer:
- if (ty->data.pointer.child_type->id == ZigTypeIdOpaque) {
- return ReqCompTimeNo;
- } else {
- return type_requires_comptime(g, ty->data.pointer.child_type);
- }
- case ZigTypeIdFn:
- return ty->data.fn.is_generic ? ReqCompTimeYes : ReqCompTimeNo;
- case ZigTypeIdOpaque:
- case ZigTypeIdEnum:
- case ZigTypeIdErrorSet:
- case ZigTypeIdBool:
- case ZigTypeIdInt:
- case ZigTypeIdVector:
- case ZigTypeIdFloat:
- case ZigTypeIdVoid:
- case ZigTypeIdUnreachable:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- return ReqCompTimeNo;
- }
- zig_unreachable();
-}
-
-void init_const_str_lit(CodeGen *g, ZigValue *const_val, Buf *str) {
- auto entry = g->string_literals_table.maybe_get(str);
- if (entry != nullptr) {
- memcpy(const_val, entry->value, sizeof(ZigValue));
- return;
- }
-
- // first we build the underlying array
- ZigValue *array_val = g->pass1_arena->create();
- array_val->special = ConstValSpecialStatic;
- array_val->type = get_array_type(g, g->builtin_types.entry_u8, buf_len(str), g->intern.for_zero_byte());
- array_val->data.x_array.special = ConstArraySpecialBuf;
- array_val->data.x_array.data.s_buf = str;
-
- // then make the pointer point to it
- const_val->special = ConstValSpecialStatic;
- const_val->type = get_pointer_to_type_extra2(g, array_val->type, true, false,
- PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, nullptr);
- const_val->data.x_ptr.special = ConstPtrSpecialRef;
- const_val->data.x_ptr.data.ref.pointee = array_val;
-
- g->string_literals_table.put(str, const_val);
-}
-
-ZigValue *create_const_str_lit(CodeGen *g, Buf *str) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_str_lit(g, const_val, str);
- return const_val;
-}
-
-void init_const_bigint(ZigValue *const_val, ZigType *type, const BigInt *bigint) {
- const_val->special = ConstValSpecialStatic;
- const_val->type = type;
- bigint_init_bigint(&const_val->data.x_bigint, bigint);
-}
-
-ZigValue *create_const_bigint(CodeGen *g, ZigType *type, const BigInt *bigint) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_bigint(const_val, type, bigint);
- return const_val;
-}
-
-
-void init_const_unsigned_negative(ZigValue *const_val, ZigType *type, uint64_t x, bool negative) {
- const_val->special = ConstValSpecialStatic;
- const_val->type = type;
- bigint_init_unsigned(&const_val->data.x_bigint, x);
- const_val->data.x_bigint.is_negative = negative;
-}
-
-ZigValue *create_const_unsigned_negative(CodeGen *g, ZigType *type, uint64_t x, bool negative) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_unsigned_negative(const_val, type, x, negative);
- return const_val;
-}
-
-void init_const_usize(CodeGen *g, ZigValue *const_val, uint64_t x) {
- return init_const_unsigned_negative(const_val, g->builtin_types.entry_usize, x, false);
-}
-
-ZigValue *create_const_usize(CodeGen *g, uint64_t x) {
- return create_const_unsigned_negative(g, g->builtin_types.entry_usize, x, false);
-}
-
-void init_const_signed(ZigValue *const_val, ZigType *type, int64_t x) {
- const_val->special = ConstValSpecialStatic;
- const_val->type = type;
- bigint_init_signed(&const_val->data.x_bigint, x);
-}
-
-ZigValue *create_const_signed(CodeGen *g, ZigType *type, int64_t x) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_signed(const_val, type, x);
- return const_val;
-}
-
-void init_const_null(ZigValue *const_val, ZigType *type) {
- const_val->special = ConstValSpecialStatic;
- const_val->type = type;
- const_val->data.x_optional = nullptr;
-}
-
-ZigValue *create_const_null(CodeGen *g, ZigType *type) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_null(const_val, type);
- return const_val;
-}
-
-void init_const_fn(ZigValue *const_val, ZigFn *fn) {
- const_val->special = ConstValSpecialStatic;
- const_val->type = fn->type_entry;
- const_val->data.x_ptr.special = ConstPtrSpecialFunction;
- const_val->data.x_ptr.data.fn.fn_entry = fn;
-}
-
-ZigValue *create_const_fn(CodeGen *g, ZigFn *fn) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_fn(const_val, fn);
- return const_val;
-}
-
-void init_const_float(ZigValue *const_val, ZigType *type, double value) {
- const_val->special = ConstValSpecialStatic;
- const_val->type = type;
- if (type->id == ZigTypeIdComptimeFloat) {
- bigfloat_init_64(&const_val->data.x_bigfloat, value);
- } else if (type->id == ZigTypeIdFloat) {
- switch (type->data.floating.bit_count) {
- case 16:
- const_val->data.x_f16 = zig_double_to_f16(value);
- break;
- case 32:
- const_val->data.x_f32 = value;
- break;
- case 64:
- const_val->data.x_f64 = value;
- break;
- case 128:
- // if we need this, we should add a function that accepts a float128_t param
- zig_unreachable();
- default:
- zig_unreachable();
- }
- } else {
- zig_unreachable();
- }
-}
-
-ZigValue *create_const_float(CodeGen *g, ZigType *type, double value) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_float(const_val, type, value);
- return const_val;
-}
-
-void init_const_enum(ZigValue *const_val, ZigType *type, const BigInt *tag) {
- const_val->special = ConstValSpecialStatic;
- const_val->type = type;
- bigint_init_bigint(&const_val->data.x_enum_tag, tag);
-}
-
-ZigValue *create_const_enum(CodeGen *g, ZigType *type, const BigInt *tag) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_enum(const_val, type, tag);
- return const_val;
-}
-
-
-void init_const_bool(CodeGen *g, ZigValue *const_val, bool value) {
- const_val->special = ConstValSpecialStatic;
- const_val->type = g->builtin_types.entry_bool;
- const_val->data.x_bool = value;
-}
-
-ZigValue *create_const_bool(CodeGen *g, bool value) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_bool(g, const_val, value);
- return const_val;
-}
-
-void init_const_runtime(ZigValue *const_val, ZigType *type) {
- const_val->special = ConstValSpecialRuntime;
- const_val->type = type;
-}
-
-ZigValue *create_const_runtime(CodeGen *g, ZigType *type) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_runtime(const_val, type);
- return const_val;
-}
-
-void init_const_type(CodeGen *g, ZigValue *const_val, ZigType *type_value) {
- const_val->special = ConstValSpecialStatic;
- const_val->type = g->builtin_types.entry_type;
- const_val->data.x_type = type_value;
-}
-
-ZigValue *create_const_type(CodeGen *g, ZigType *type_value) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_type(g, const_val, type_value);
- return const_val;
-}
-
-void init_const_slice(CodeGen *g, ZigValue *const_val, ZigValue *array_val,
- size_t start, size_t len, bool is_const)
-{
- assert(array_val->type->id == ZigTypeIdArray);
-
- ZigType *ptr_type = get_pointer_to_type_extra(g, array_val->type->data.array.child_type,
- is_const, false, PtrLenUnknown, 0, 0, 0, false);
-
- const_val->special = ConstValSpecialStatic;
- const_val->type = get_slice_type(g, ptr_type);
- const_val->data.x_struct.fields = alloc_const_vals_ptrs(g, 2);
-
- init_const_ptr_array(g, const_val->data.x_struct.fields[slice_ptr_index], array_val, start, is_const,
- PtrLenUnknown);
- init_const_usize(g, const_val->data.x_struct.fields[slice_len_index], len);
-}
-
-ZigValue *create_const_slice(CodeGen *g, ZigValue *array_val, size_t start, size_t len, bool is_const) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_slice(g, const_val, array_val, start, len, is_const);
- return const_val;
-}
-
-void init_const_ptr_array(CodeGen *g, ZigValue *const_val, ZigValue *array_val,
- size_t elem_index, bool is_const, PtrLen ptr_len)
-{
- assert(array_val->type->id == ZigTypeIdArray);
- ZigType *child_type = array_val->type->data.array.child_type;
-
- const_val->special = ConstValSpecialStatic;
- const_val->type = get_pointer_to_type_extra(g, child_type, is_const, false,
- ptr_len, 0, 0, 0, false);
- const_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
- const_val->data.x_ptr.data.base_array.array_val = array_val;
- const_val->data.x_ptr.data.base_array.elem_index = elem_index;
-}
-
-ZigValue *create_const_ptr_array(CodeGen *g, ZigValue *array_val, size_t elem_index, bool is_const,
- PtrLen ptr_len)
-{
- ZigValue *const_val = g->pass1_arena->create();
- init_const_ptr_array(g, const_val, array_val, elem_index, is_const, ptr_len);
- return const_val;
-}
-
-void init_const_ptr_ref(CodeGen *g, ZigValue *const_val, ZigValue *pointee_val, bool is_const) {
- const_val->special = ConstValSpecialStatic;
- const_val->type = get_pointer_to_type(g, pointee_val->type, is_const);
- const_val->data.x_ptr.special = ConstPtrSpecialRef;
- const_val->data.x_ptr.data.ref.pointee = pointee_val;
-}
-
-ZigValue *create_const_ptr_ref(CodeGen *g, ZigValue *pointee_val, bool is_const) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_ptr_ref(g, const_val, pointee_val, is_const);
- return const_val;
-}
-
-void init_const_ptr_hard_coded_addr(CodeGen *g, ZigValue *const_val, ZigType *pointee_type,
- size_t addr, bool is_const)
-{
- const_val->special = ConstValSpecialStatic;
- const_val->type = get_pointer_to_type(g, pointee_type, is_const);
- const_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
- const_val->data.x_ptr.data.hard_coded_addr.addr = addr;
-}
-
-ZigValue *create_const_ptr_hard_coded_addr(CodeGen *g, ZigType *pointee_type,
- size_t addr, bool is_const)
-{
- ZigValue *const_val = g->pass1_arena->create();
- init_const_ptr_hard_coded_addr(g, const_val, pointee_type, addr, is_const);
- return const_val;
-}
-
-ZigValue **alloc_const_vals_ptrs(CodeGen *g, size_t count) {
- return realloc_const_vals_ptrs(g, nullptr, 0, count);
-}
-
-ZigValue **realloc_const_vals_ptrs(CodeGen *g, ZigValue **ptr, size_t old_count, size_t new_count) {
- assert(new_count >= old_count);
-
- size_t new_item_count = new_count - old_count;
- ZigValue **result = heap::c_allocator.reallocate(ptr, old_count, new_count);
- ZigValue *vals = g->pass1_arena->allocate(new_item_count);
- for (size_t i = old_count; i < new_count; i += 1) {
- result[i] = &vals[i - old_count];
- }
- return result;
-}
-
-TypeStructField **alloc_type_struct_fields(size_t count) {
- return realloc_type_struct_fields(nullptr, 0, count);
-}
-
-TypeStructField **realloc_type_struct_fields(TypeStructField **ptr, size_t old_count, size_t new_count) {
- assert(new_count >= old_count);
-
- size_t new_item_count = new_count - old_count;
- TypeStructField **result = heap::c_allocator.reallocate(ptr, old_count, new_count);
- TypeStructField *vals = heap::c_allocator.allocate(new_item_count);
- for (size_t i = old_count; i < new_count; i += 1) {
- result[i] = &vals[i - old_count];
- }
- return result;
-}
-
-static ZigType *get_async_fn_type(CodeGen *g, ZigType *orig_fn_type) {
- if (orig_fn_type->data.fn.fn_type_id.cc == CallingConventionAsync)
- return orig_fn_type;
-
- ZigType *fn_type = heap::c_allocator.allocate_nonzero(1);
- *fn_type = *orig_fn_type;
- fn_type->data.fn.fn_type_id.cc = CallingConventionAsync;
- fn_type->llvm_type = nullptr;
- fn_type->llvm_di_type = nullptr;
-
- return fn_type;
-}
-
-// Traverse up to the very top ExprScope, which has children.
-// We have just arrived at the top from a child. That child,
-// and its next siblings, do not need to be marked. But the previous
-// siblings do.
-// x + (await y)
-// vs
-// (await y) + x
-static void mark_suspension_point(Scope *scope) {
- ScopeExpr *child_expr_scope = (scope->id == ScopeIdExpr) ? reinterpret_cast(scope) : nullptr;
- bool looking_for_exprs = true;
- for (;;) {
- scope = scope->parent;
- switch (scope->id) {
- case ScopeIdDeferExpr:
- case ScopeIdDecls:
- case ScopeIdFnDef:
- case ScopeIdCompTime:
- case ScopeIdNoSuspend:
- case ScopeIdCImport:
- case ScopeIdSuspend:
- case ScopeIdTypeOf:
- return;
- case ScopeIdVarDecl:
- case ScopeIdDefer:
- case ScopeIdBlock:
- looking_for_exprs = false;
- continue;
- case ScopeIdRuntime:
- continue;
- case ScopeIdLoop: {
- ScopeLoop *loop_scope = reinterpret_cast(scope);
- if (loop_scope->spill_scope != nullptr) {
- loop_scope->spill_scope->need_spill = MemoizedBoolTrue;
- }
- looking_for_exprs = false;
- continue;
- }
- case ScopeIdExpr: {
- ScopeExpr *parent_expr_scope = reinterpret_cast(scope);
- if (!looking_for_exprs) {
- if (parent_expr_scope->spill_harder) {
- parent_expr_scope->need_spill = MemoizedBoolTrue;
- }
- // Now we're only looking for a block, to see if it's in a loop (see the case ScopeIdBlock)
- continue;
- }
- if (child_expr_scope != nullptr) {
- for (size_t i = 0; parent_expr_scope->children_ptr[i] != child_expr_scope; i += 1) {
- assert(i < parent_expr_scope->children_len);
- parent_expr_scope->children_ptr[i]->need_spill = MemoizedBoolTrue;
- }
- }
- parent_expr_scope->need_spill = MemoizedBoolTrue;
- child_expr_scope = parent_expr_scope;
- continue;
- }
- }
- }
-}
-
-static bool scope_needs_spill(Scope *scope) {
- ScopeExpr *scope_expr = find_expr_scope(scope);
- if (scope_expr == nullptr) return false;
-
- switch (scope_expr->need_spill) {
- case MemoizedBoolUnknown:
- if (scope_needs_spill(scope_expr->base.parent)) {
- scope_expr->need_spill = MemoizedBoolTrue;
- return true;
- } else {
- scope_expr->need_spill = MemoizedBoolFalse;
- return false;
- }
- case MemoizedBoolFalse:
- return false;
- case MemoizedBoolTrue:
- return true;
- }
- zig_unreachable();
-}
-
-static ZigType *resolve_type_isf(ZigType *ty) {
- if (ty->id != ZigTypeIdPointer) return ty;
- InferredStructField *isf = ty->data.pointer.inferred_struct_field;
- if (isf == nullptr) return ty;
- TypeStructField *field = find_struct_type_field(isf->inferred_struct_type, isf->field_name);
- assert(field != nullptr);
- return field->type_entry;
-}
-
-static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
- Error err;
-
- if (frame_type->data.frame.locals_struct != nullptr)
- return ErrorNone;
-
- ZigFn *fn = frame_type->data.frame.fn;
- assert(!fn->type_entry->data.fn.is_generic);
-
- if (frame_type->data.frame.resolve_loop_type != nullptr) {
- if (!frame_type->data.frame.reported_loop_err) {
- add_node_error(g, fn->proto_node,
- buf_sprintf("'%s' depends on itself", buf_ptr(&frame_type->name)));
- }
- return ErrorSemanticAnalyzeFail;
- }
-
- switch (fn->anal_state) {
- case FnAnalStateInvalid:
- return ErrorSemanticAnalyzeFail;
- case FnAnalStateComplete:
- break;
- case FnAnalStateReady:
- analyze_fn_body(g, fn);
- if (fn->anal_state == FnAnalStateInvalid)
- return ErrorSemanticAnalyzeFail;
- break;
- case FnAnalStateProbing: {
- add_node_error(g, fn->proto_node,
- buf_sprintf("cannot resolve '%s': function not fully analyzed yet",
- buf_ptr(&frame_type->name)));
- return ErrorSemanticAnalyzeFail;
- }
- }
- analyze_fn_async(g, fn, false);
- if (fn->anal_state == FnAnalStateInvalid)
- return ErrorSemanticAnalyzeFail;
-
- if (!fn_is_async(fn)) {
- ZigType *fn_type = fn->type_entry;
- FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
- ZigType *ptr_return_type = get_pointer_to_type(g, fn_type_id->return_type, false);
-
- // label (grep this): [fn_frame_struct_layout]
- ZigList fields = {};
-
- fields.append({"@fn_ptr", g->builtin_types.entry_usize, 0});
- fields.append({"@resume_index", g->builtin_types.entry_usize, 0});
- fields.append({"@awaiter", g->builtin_types.entry_usize, 0});
-
- fields.append({"@result_ptr_callee", ptr_return_type, 0});
- fields.append({"@result_ptr_awaiter", ptr_return_type, 0});
- fields.append({"@result", fn_type_id->return_type, 0});
-
- if (codegen_fn_has_err_ret_tracing_arg(g, fn_type_id->return_type)) {
- ZigType *ptr_to_stack_trace_type = get_pointer_to_type(g, get_stack_trace_type(g), false);
- fields.append({"@ptr_stack_trace_callee", ptr_to_stack_trace_type, 0});
- fields.append({"@ptr_stack_trace_awaiter", ptr_to_stack_trace_type, 0});
-
- fields.append({"@stack_trace", get_stack_trace_type(g), 0});
- fields.append({"@instruction_addresses",
- get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count, nullptr), 0});
- }
-
- frame_type->data.frame.locals_struct = get_struct_type(g, buf_ptr(&frame_type->name),
- fields.items, fields.length, target_fn_align(g->zig_target));
- frame_type->abi_size = frame_type->data.frame.locals_struct->abi_size;
- frame_type->abi_align = frame_type->data.frame.locals_struct->abi_align;
- frame_type->size_in_bits = frame_type->data.frame.locals_struct->size_in_bits;
-
- return ErrorNone;
- }
-
- ZigType *fn_type = get_async_fn_type(g, fn->type_entry);
-
- if (fn->analyzed_executable.need_err_code_spill) {
- IrInstGenAlloca *alloca_gen = heap::c_allocator.create();
- alloca_gen->base.id = IrInstGenIdAlloca;
- alloca_gen->base.base.source_node = fn->proto_node;
- alloca_gen->base.base.scope = fn->child_scope;
- alloca_gen->base.value = g->pass1_arena->create();
- alloca_gen->base.value->type = get_pointer_to_type(g, g->builtin_types.entry_global_error_set, false);
- alloca_gen->base.base.ref_count = 1;
- alloca_gen->name_hint = "";
- fn->alloca_gen_list.append(alloca_gen);
- fn->err_code_spill = &alloca_gen->base;
- }
-
- ZigType *largest_call_frame_type = nullptr;
- // Later we'll change this to be largest_call_frame_type instead of void.
- IrInstGen *all_calls_alloca = ir_create_alloca(g, &fn->fndef_scope->base, fn->body_node,
- fn, g->builtin_types.entry_void, "@async_call_frame");
-
- for (size_t i = 0; i < fn->call_list.length; i += 1) {
- IrInstGenCall *call = fn->call_list.at(i);
- if (call->new_stack != nullptr) {
- // don't need to allocate a frame for this
- continue;
- }
- ZigFn *callee = call->fn_entry;
- if (callee == nullptr) {
- if (call->fn_ref->value->type->data.fn.fn_type_id.cc != CallingConventionAsync) {
- continue;
- }
- add_node_error(g, call->base.base.source_node,
- buf_sprintf("function is not comptime-known; @asyncCall required"));
- return ErrorSemanticAnalyzeFail;
- }
- if (callee->body_node == nullptr) {
- continue;
- }
- if (callee->anal_state == FnAnalStateProbing) {
- ErrorMsg *msg = add_node_error(g, fn->proto_node,
- buf_sprintf("unable to determine async function frame of '%s'", buf_ptr(&fn->symbol_name)));
- g->trace_err = add_error_note(g, msg, call->base.base.source_node,
- buf_sprintf("analysis of function '%s' depends on the frame", buf_ptr(&callee->symbol_name)));
- return ErrorSemanticAnalyzeFail;
- }
-
- ZigType *callee_frame_type = get_fn_frame_type(g, callee);
- frame_type->data.frame.resolve_loop_type = callee_frame_type;
- frame_type->data.frame.resolve_loop_src_node = call->base.base.source_node;
-
- analyze_fn_body(g, callee);
- if (callee->anal_state == FnAnalStateInvalid) {
- frame_type->data.frame.locals_struct = g->builtin_types.entry_invalid;
- return ErrorSemanticAnalyzeFail;
- }
- analyze_fn_async(g, callee, true);
- if (callee->inferred_async_node == inferred_async_checking) {
- assert(g->errors.length != 0);
- frame_type->data.frame.locals_struct = g->builtin_types.entry_invalid;
- return ErrorSemanticAnalyzeFail;
- }
- if (!fn_is_async(callee))
- continue;
-
- mark_suspension_point(call->base.base.scope);
-
- if ((err = type_resolve(g, callee_frame_type, ResolveStatusSizeKnown))) {
- return err;
- }
- if (largest_call_frame_type == nullptr ||
- callee_frame_type->abi_size > largest_call_frame_type->abi_size)
- {
- largest_call_frame_type = callee_frame_type;
- }
-
- call->frame_result_loc = all_calls_alloca;
- }
- if (largest_call_frame_type != nullptr) {
- all_calls_alloca->value->type = get_pointer_to_type(g, largest_call_frame_type, false);
- }
-
- // Since this frame is async, an await might represent a suspend point, and
- // therefore need to spill. It also needs to mark expr scopes as having to spill.
- // For example: foo() + await z
- // The funtion call result of foo() must be spilled.
- for (size_t i = 0; i < fn->await_list.length; i += 1) {
- IrInstGenAwait *await = fn->await_list.at(i);
- if (await->is_nosuspend) {
- continue;
- }
- if (await->base.value->special != ConstValSpecialRuntime) {
- // Known at comptime. No spill, no suspend.
- continue;
- }
- if (await->target_fn != nullptr) {
- // we might not need to suspend
- analyze_fn_async(g, await->target_fn, false);
- if (await->target_fn->anal_state == FnAnalStateInvalid) {
- frame_type->data.frame.locals_struct = g->builtin_types.entry_invalid;
- return ErrorSemanticAnalyzeFail;
- }
- if (!fn_is_async(await->target_fn)) {
- // This await does not represent a suspend point. No spill needed,
- // and no need to mark ExprScope.
- continue;
- }
- }
- // This await is a suspend point, but it might not need a spill.
- // We do need to mark the ExprScope as having a suspend point in it.
- mark_suspension_point(await->base.base.scope);
-
- if (await->result_loc != nullptr) {
- // If there's a result location, that is the spill
- continue;
- }
- if (await->base.base.ref_count == 0)
- continue;
- if (!type_has_bits(g, await->base.value->type))
- continue;
- await->result_loc = ir_create_alloca(g, await->base.base.scope, await->base.base.source_node, fn,
- await->base.value->type, "");
- }
- for (size_t block_i = 0; block_i < fn->analyzed_executable.basic_block_list.length; block_i += 1) {
- IrBasicBlockGen *block = fn->analyzed_executable.basic_block_list.at(block_i);
- for (size_t instr_i = 0; instr_i < block->instruction_list.length; instr_i += 1) {
- IrInstGen *instruction = block->instruction_list.at(instr_i);
- if (instruction->id == IrInstGenIdSuspendFinish) {
- mark_suspension_point(instruction->base.scope);
- }
- }
- }
- // Now that we've marked all the expr scopes that have to spill, we go over the instructions
- // and spill the relevant ones.
- for (size_t block_i = 0; block_i < fn->analyzed_executable.basic_block_list.length; block_i += 1) {
- IrBasicBlockGen *block = fn->analyzed_executable.basic_block_list.at(block_i);
- for (size_t instr_i = 0; instr_i < block->instruction_list.length; instr_i += 1) {
- IrInstGen *instruction = block->instruction_list.at(instr_i);
- if (instruction->id == IrInstGenIdAwait ||
- instruction->id == IrInstGenIdVarPtr ||
- instruction->id == IrInstGenIdAlloca ||
- instruction->id == IrInstGenIdSpillBegin ||
- instruction->id == IrInstGenIdSpillEnd)
- {
- // This instruction does its own spilling specially, or otherwise doesn't need it.
- continue;
- }
- if (instruction->id == IrInstGenIdCast &&
- reinterpret_cast(instruction)->cast_op == CastOpNoop)
- {
- // The IR instruction exists only to change the type according to Zig. No spill needed.
- continue;
- }
- if (instruction->value->special != ConstValSpecialRuntime)
- continue;
- if (instruction->base.ref_count == 0)
- continue;
- if ((err = type_resolve(g, instruction->value->type, ResolveStatusZeroBitsKnown)))
- return ErrorSemanticAnalyzeFail;
- if (!type_has_bits(g, instruction->value->type))
- continue;
- if (scope_needs_spill(instruction->base.scope)) {
- instruction->spill = ir_create_alloca(g, instruction->base.scope, instruction->base.source_node,
- fn, instruction->value->type, "");
- }
- }
- }
-
- FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
- ZigType *ptr_return_type = get_pointer_to_type(g, fn_type_id->return_type, false);
-
- // label (grep this): [fn_frame_struct_layout]
- ZigList fields = {};
-
- fields.append({"@fn_ptr", fn_type, 0});
- fields.append({"@resume_index", g->builtin_types.entry_usize, 0});
- fields.append({"@awaiter", g->builtin_types.entry_usize, 0});
-
- fields.append({"@result_ptr_callee", ptr_return_type, 0});
- fields.append({"@result_ptr_awaiter", ptr_return_type, 0});
- fields.append({"@result", fn_type_id->return_type, 0});
-
- if (codegen_fn_has_err_ret_tracing_arg(g, fn_type_id->return_type)) {
- ZigType *ptr_stack_trace_type = get_pointer_to_type(g, get_stack_trace_type(g), false);
- fields.append({"@ptr_stack_trace_callee", ptr_stack_trace_type, 0});
- fields.append({"@ptr_stack_trace_awaiter", ptr_stack_trace_type, 0});
- }
-
- for (size_t arg_i = 0; arg_i < fn_type_id->param_count; arg_i += 1) {
- FnTypeParamInfo *param_info = &fn_type_id->param_info[arg_i];
- AstNode *param_decl_node = get_param_decl_node(fn, arg_i);
- Buf *param_name;
- bool is_var_args = param_decl_node && param_decl_node->data.param_decl.is_var_args;
- if (param_decl_node && !is_var_args) {
- param_name = param_decl_node->data.param_decl.name;
- } else {
- param_name = buf_sprintf("@arg%" ZIG_PRI_usize, arg_i);
- }
- ZigType *param_type = resolve_type_isf(param_info->type);
- if ((err = type_resolve(g, param_type, ResolveStatusSizeKnown))) {
- return err;
- }
-
- fields.append({buf_ptr(param_name), param_type, 0});
- }
-
- if (codegen_fn_has_err_ret_tracing_stack(g, fn, true)) {
- fields.append({"@stack_trace", get_stack_trace_type(g), 0});
- fields.append({"@instruction_addresses",
- get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count, nullptr), 0});
- }
-
- for (size_t alloca_i = 0; alloca_i < fn->alloca_gen_list.length; alloca_i += 1) {
- IrInstGenAlloca *instruction = fn->alloca_gen_list.at(alloca_i);
- instruction->field_index = SIZE_MAX;
- ZigType *ptr_type = instruction->base.value->type;
- assert(ptr_type->id == ZigTypeIdPointer);
- ZigType *child_type = resolve_type_isf(ptr_type->data.pointer.child_type);
- if (!type_has_bits(g, child_type))
- continue;
- if (instruction->base.base.ref_count == 0)
- continue;
- if (instruction->base.value->special != ConstValSpecialRuntime) {
- if (const_ptr_pointee(nullptr, g, instruction->base.value, nullptr)->special !=
- ConstValSpecialRuntime)
- {
- continue;
- }
- }
-
- frame_type->data.frame.resolve_loop_type = child_type;
- frame_type->data.frame.resolve_loop_src_node = instruction->base.base.source_node;
- if ((err = type_resolve(g, child_type, ResolveStatusSizeKnown))) {
- return err;
- }
-
- const char *name;
- if (*instruction->name_hint == 0) {
- name = buf_ptr(buf_sprintf("@local%" ZIG_PRI_usize, alloca_i));
- } else {
- name = buf_ptr(buf_sprintf("%s.%" ZIG_PRI_usize, instruction->name_hint, alloca_i));
- }
- instruction->field_index = fields.length;
-
- fields.append({name, child_type, instruction->align});
- }
-
-
- frame_type->data.frame.locals_struct = get_struct_type(g, buf_ptr(&frame_type->name),
- fields.items, fields.length, target_fn_align(g->zig_target));
- frame_type->abi_size = frame_type->data.frame.locals_struct->abi_size;
- frame_type->abi_align = frame_type->data.frame.locals_struct->abi_align;
- frame_type->size_in_bits = frame_type->data.frame.locals_struct->size_in_bits;
-
- if (g->largest_frame_fn == nullptr || frame_type->abi_size > g->largest_frame_fn->frame_type->abi_size) {
- g->largest_frame_fn = fn;
- }
-
- return ErrorNone;
-}
-
-static Error resolve_pointer_zero_bits(CodeGen *g, ZigType *ty) {
- Error err;
-
- if (ty->abi_size != SIZE_MAX)
- return ErrorNone;
-
- if (ty->data.pointer.resolve_loop_flag_zero_bits) {
- ty->abi_size = g->builtin_types.entry_usize->abi_size;
- ty->size_in_bits = g->builtin_types.entry_usize->size_in_bits;
- ty->abi_align = g->builtin_types.entry_usize->abi_align;
- return ErrorNone;
- }
- ty->data.pointer.resolve_loop_flag_zero_bits = true;
-
- ZigType *elem_type;
- InferredStructField *isf = ty->data.pointer.inferred_struct_field;
- if (isf != nullptr) {
- TypeStructField *field = find_struct_type_field(isf->inferred_struct_type, isf->field_name);
- assert(field != nullptr);
- if (field->is_comptime) {
- ty->abi_size = 0;
- ty->size_in_bits = 0;
- ty->abi_align = 0;
- return ErrorNone;
- }
- elem_type = field->type_entry;
- } else {
- elem_type = ty->data.pointer.child_type;
- }
-
- bool has_bits;
- if ((err = type_has_bits2(g, elem_type, &has_bits)))
- return err;
-
- if (has_bits) {
- ty->abi_size = g->builtin_types.entry_usize->abi_size;
- ty->size_in_bits = g->builtin_types.entry_usize->size_in_bits;
- ty->abi_align = g->builtin_types.entry_usize->abi_align;
- } else {
- ty->abi_size = 0;
- ty->size_in_bits = 0;
- ty->abi_align = 0;
- }
- return ErrorNone;
-}
-
-Error type_resolve(CodeGen *g, ZigType *ty, ResolveStatus status) {
- if (type_is_invalid(ty))
- return ErrorSemanticAnalyzeFail;
- switch (status) {
- case ResolveStatusUnstarted:
- return ErrorNone;
- case ResolveStatusBeingInferred:
- zig_unreachable();
- case ResolveStatusInvalid:
- zig_unreachable();
- case ResolveStatusZeroBitsKnown:
- switch (ty->id) {
- case ZigTypeIdStruct:
- return resolve_struct_zero_bits(g, ty);
- case ZigTypeIdEnum:
- return resolve_enum_zero_bits(g, ty);
- case ZigTypeIdUnion:
- return resolve_union_zero_bits(g, ty);
- case ZigTypeIdPointer:
- return resolve_pointer_zero_bits(g, ty);
- default:
- return ErrorNone;
- }
- case ResolveStatusAlignmentKnown:
- switch (ty->id) {
- case ZigTypeIdStruct:
- return resolve_struct_alignment(g, ty);
- case ZigTypeIdEnum:
- return resolve_enum_zero_bits(g, ty);
- case ZigTypeIdUnion:
- return resolve_union_alignment(g, ty);
- case ZigTypeIdFnFrame:
- return resolve_async_frame(g, ty);
- case ZigTypeIdPointer:
- return resolve_pointer_zero_bits(g, ty);
- default:
- return ErrorNone;
- }
- case ResolveStatusSizeKnown:
- switch (ty->id) {
- case ZigTypeIdStruct:
- return resolve_struct_type(g, ty);
- case ZigTypeIdEnum:
- return resolve_enum_zero_bits(g, ty);
- case ZigTypeIdUnion:
- return resolve_union_type(g, ty);
- case ZigTypeIdFnFrame:
- return resolve_async_frame(g, ty);
- case ZigTypeIdPointer:
- return resolve_pointer_zero_bits(g, ty);
- default:
- return ErrorNone;
- }
- case ResolveStatusLLVMFwdDecl:
- case ResolveStatusLLVMFull:
- resolve_llvm_types(g, ty, status);
- return ErrorNone;
- }
- zig_unreachable();
-}
-
-bool ir_get_var_is_comptime(ZigVar *var) {
- if (var->is_comptime_memoized)
- return var->is_comptime_memoized_value;
-
- var->is_comptime_memoized = true;
-
- // The is_comptime field can be left null, which means not comptime.
- if (var->is_comptime == nullptr) {
- var->is_comptime_memoized_value = false;
- return var->is_comptime_memoized_value;
- }
- // When the is_comptime field references an instruction that has to get analyzed, this
- // is the value.
- if (var->is_comptime->child != nullptr) {
- assert(var->is_comptime->child->value->type->id == ZigTypeIdBool);
- var->is_comptime_memoized_value = var->is_comptime->child->value->data.x_bool;
- var->is_comptime = nullptr;
- return var->is_comptime_memoized_value;
- }
- // As an optimization, is_comptime values which are constant are allowed
- // to be omitted from analysis. In this case, there is no child instruction
- // and we simply look at the unanalyzed const parent instruction.
- assert(var->is_comptime->id == IrInstSrcIdConst);
- IrInstSrcConst *const_inst = reinterpret_cast(var->is_comptime);
- assert(const_inst->value->type->id == ZigTypeIdBool);
- var->is_comptime_memoized_value = const_inst->value->data.x_bool;
- var->is_comptime = nullptr;
- return var->is_comptime_memoized_value;
-}
-
-bool const_values_equal_ptr(ZigValue *a, ZigValue *b) {
- if (a->data.x_ptr.special != b->data.x_ptr.special)
- return false;
- switch (a->data.x_ptr.special) {
- case ConstPtrSpecialInvalid:
- zig_unreachable();
- case ConstPtrSpecialRef:
- if (a->data.x_ptr.data.ref.pointee != b->data.x_ptr.data.ref.pointee)
- return false;
- return true;
- case ConstPtrSpecialBaseArray:
- case ConstPtrSpecialSubArray:
- if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val) {
- return false;
- }
- if (a->data.x_ptr.data.base_array.elem_index != b->data.x_ptr.data.base_array.elem_index)
- return false;
- return true;
- case ConstPtrSpecialBaseStruct:
- if (a->data.x_ptr.data.base_struct.struct_val != b->data.x_ptr.data.base_struct.struct_val) {
- return false;
- }
- if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index)
- return false;
- return true;
- case ConstPtrSpecialBaseErrorUnionCode:
- if (a->data.x_ptr.data.base_err_union_code.err_union_val !=
- b->data.x_ptr.data.base_err_union_code.err_union_val)
- {
- return false;
- }
- return true;
- case ConstPtrSpecialBaseErrorUnionPayload:
- if (a->data.x_ptr.data.base_err_union_payload.err_union_val !=
- b->data.x_ptr.data.base_err_union_payload.err_union_val)
- {
- return false;
- }
- return true;
- case ConstPtrSpecialBaseOptionalPayload:
- if (a->data.x_ptr.data.base_optional_payload.optional_val !=
- b->data.x_ptr.data.base_optional_payload.optional_val)
- {
- return false;
- }
- return true;
- case ConstPtrSpecialHardCodedAddr:
- if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr)
- return false;
- return true;
- case ConstPtrSpecialDiscard:
- return true;
- case ConstPtrSpecialFunction:
- return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry;
- case ConstPtrSpecialNull:
- return true;
- }
- zig_unreachable();
-}
-
-static bool const_values_equal_array(CodeGen *g, ZigValue *a, ZigValue *b, size_t len) {
- if (a->data.x_array.special == ConstArraySpecialUndef &&
- b->data.x_array.special == ConstArraySpecialUndef)
- {
- return true;
- }
- if (a->data.x_array.special == ConstArraySpecialUndef ||
- b->data.x_array.special == ConstArraySpecialUndef)
- {
- return false;
- }
- if (a->data.x_array.special == ConstArraySpecialBuf &&
- b->data.x_array.special == ConstArraySpecialBuf)
- {
- return buf_eql_buf(a->data.x_array.data.s_buf, b->data.x_array.data.s_buf);
- }
- expand_undef_array(g, a);
- expand_undef_array(g, b);
-
- ZigValue *a_elems = a->data.x_array.data.s_none.elements;
- ZigValue *b_elems = b->data.x_array.data.s_none.elements;
-
- for (size_t i = 0; i < len; i += 1) {
- if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
- return false;
- }
-
- return true;
-}
-
-bool const_values_equal(CodeGen *g, ZigValue *a, ZigValue *b) {
- if (a->type->id != b->type->id) return false;
- if (a->type == b->type) {
- switch (type_has_one_possible_value(g, a->type)) {
- case OnePossibleValueInvalid:
- zig_unreachable();
- case OnePossibleValueNo:
- break;
- case OnePossibleValueYes:
- return true;
- }
- }
- if (a->special == ConstValSpecialUndef || b->special == ConstValSpecialUndef) {
- return a->special == b->special;
- }
- assert(a->special == ConstValSpecialStatic);
- assert(b->special == ConstValSpecialStatic);
- switch (a->type->id) {
- case ZigTypeIdOpaque:
- zig_unreachable();
- case ZigTypeIdEnum:
- return bigint_cmp(&a->data.x_enum_tag, &b->data.x_enum_tag) == CmpEQ;
- case ZigTypeIdUnion: {
- ConstUnionValue *union1 = &a->data.x_union;
- ConstUnionValue *union2 = &b->data.x_union;
-
- if (bigint_cmp(&union1->tag, &union2->tag) == CmpEQ) {
- TypeUnionField *field = find_union_field_by_tag(a->type, &union1->tag);
- assert(field != nullptr);
- if (!type_has_bits(g, field->type_entry))
- return true;
- assert(find_union_field_by_tag(a->type, &union2->tag) != nullptr);
- return const_values_equal(g, union1->payload, union2->payload);
- }
- return false;
- }
- case ZigTypeIdMetaType:
- return a->data.x_type == b->data.x_type;
- case ZigTypeIdVoid:
- return true;
- case ZigTypeIdErrorSet:
- return a->data.x_err_set->value == b->data.x_err_set->value;
- case ZigTypeIdBool:
- return a->data.x_bool == b->data.x_bool;
- case ZigTypeIdFloat:
- assert(a->type->data.floating.bit_count == b->type->data.floating.bit_count);
- switch (a->type->data.floating.bit_count) {
- case 16:
- return f16_eq(a->data.x_f16, b->data.x_f16);
- case 32:
- return a->data.x_f32 == b->data.x_f32;
- case 64:
- return a->data.x_f64 == b->data.x_f64;
- case 128:
- return f128M_eq(&a->data.x_f128, &b->data.x_f128);
- default:
- zig_unreachable();
- }
- case ZigTypeIdComptimeFloat:
- return bigfloat_cmp(&a->data.x_bigfloat, &b->data.x_bigfloat) == CmpEQ;
- case ZigTypeIdInt:
- case ZigTypeIdComptimeInt:
- return bigint_cmp(&a->data.x_bigint, &b->data.x_bigint) == CmpEQ;
- case ZigTypeIdEnumLiteral:
- return buf_eql_buf(a->data.x_enum_literal, b->data.x_enum_literal);
- case ZigTypeIdPointer:
- case ZigTypeIdFn:
- return const_values_equal_ptr(a, b);
- case ZigTypeIdVector:
- assert(a->type->data.vector.len == b->type->data.vector.len);
- return const_values_equal_array(g, a, b, a->type->data.vector.len);
- case ZigTypeIdArray: {
- assert(a->type->data.array.len == b->type->data.array.len);
- return const_values_equal_array(g, a, b, a->type->data.array.len);
- }
- case ZigTypeIdStruct:
- for (size_t i = 0; i < a->type->data.structure.src_field_count; i += 1) {
- ZigValue *field_a = a->data.x_struct.fields[i];
- ZigValue *field_b = b->data.x_struct.fields[i];
- if (!const_values_equal(g, field_a, field_b))
- return false;
- }
- return true;
- case ZigTypeIdFnFrame:
- zig_panic("TODO");
- case ZigTypeIdAnyFrame:
- zig_panic("TODO");
- case ZigTypeIdUndefined:
- zig_panic("TODO");
- case ZigTypeIdNull:
- zig_panic("TODO");
- case ZigTypeIdOptional:
- if (get_src_ptr_type(a->type) != nullptr)
- return const_values_equal_ptr(a, b);
- if (a->data.x_optional == nullptr || b->data.x_optional == nullptr) {
- return (a->data.x_optional == nullptr && b->data.x_optional == nullptr);
- } else {
- return const_values_equal(g, a->data.x_optional, b->data.x_optional);
- }
- case ZigTypeIdErrorUnion:
- zig_panic("TODO");
- case ZigTypeIdBoundFn:
- case ZigTypeIdInvalid:
- case ZigTypeIdUnreachable:
- zig_unreachable();
- }
- zig_unreachable();
-}
-
-void eval_min_max_value_int(CodeGen *g, ZigType *int_type, BigInt *bigint, bool is_max) {
- assert(int_type->id == ZigTypeIdInt);
- if (int_type->data.integral.bit_count == 0) {
- bigint_init_unsigned(bigint, 0);
- return;
- }
- if (is_max) {
- // is_signed=true (1 << (bit_count - 1)) - 1
- // is_signed=false (1 << (bit_count - 0)) - 1
- BigInt one = {0};
- bigint_init_unsigned(&one, 1);
-
- size_t shift_amt = int_type->data.integral.bit_count - (int_type->data.integral.is_signed ? 1 : 0);
- BigInt bit_count_bi = {0};
- bigint_init_unsigned(&bit_count_bi, shift_amt);
-
- BigInt shifted_bi = {0};
- bigint_shl(&shifted_bi, &one, &bit_count_bi);
-
- bigint_sub(bigint, &shifted_bi, &one);
- } else if (int_type->data.integral.is_signed) {
- // - (1 << (bit_count - 1))
- BigInt one = {0};
- bigint_init_unsigned(&one, 1);
-
- BigInt bit_count_bi = {0};
- bigint_init_unsigned(&bit_count_bi, int_type->data.integral.bit_count - 1);
-
- BigInt shifted_bi = {0};
- bigint_shl(&shifted_bi, &one, &bit_count_bi);
-
- bigint_negate(bigint, &shifted_bi);
- } else {
- bigint_init_unsigned(bigint, 0);
- }
-}
-
-void eval_min_max_value(CodeGen *g, ZigType *type_entry, ZigValue *const_val, bool is_max) {
- if (type_entry->id == ZigTypeIdInt) {
- const_val->special = ConstValSpecialStatic;
- eval_min_max_value_int(g, type_entry, &const_val->data.x_bigint, is_max);
- } else if (type_entry->id == ZigTypeIdBool) {
- const_val->special = ConstValSpecialStatic;
- const_val->data.x_bool = is_max;
- } else if (type_entry->id == ZigTypeIdVoid) {
- // nothing to do
- } else {
- zig_unreachable();
- }
-}
-
-static void render_const_val_ptr(CodeGen *g, Buf *buf, ZigValue *const_val, ZigType *type_entry) {
- if (type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.child_type->id == ZigTypeIdOpaque) {
- buf_append_buf(buf, &type_entry->name);
- return;
- }
-
- switch (const_val->data.x_ptr.special) {
- case ConstPtrSpecialInvalid:
- zig_unreachable();
- case ConstPtrSpecialRef:
- case ConstPtrSpecialBaseStruct:
- case ConstPtrSpecialBaseErrorUnionCode:
- case ConstPtrSpecialBaseErrorUnionPayload:
- case ConstPtrSpecialBaseOptionalPayload:
- buf_appendf(buf, "*");
- // TODO we need a source node for const_ptr_pointee because it can generate compile errors
- render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr));
- return;
- case ConstPtrSpecialBaseArray:
- case ConstPtrSpecialSubArray:
- buf_appendf(buf, "*");
- // TODO we need a source node for const_ptr_pointee because it can generate compile errors
- render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr));
- return;
- case ConstPtrSpecialHardCodedAddr:
- buf_appendf(buf, "(%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->name),
- const_val->data.x_ptr.data.hard_coded_addr.addr);
- return;
- case ConstPtrSpecialDiscard:
- buf_append_str(buf, "*_");
- return;
- case ConstPtrSpecialFunction:
- {
- ZigFn *fn_entry = const_val->data.x_ptr.data.fn.fn_entry;
- buf_appendf(buf, "@ptrCast(%s, %s)", buf_ptr(&const_val->type->name), buf_ptr(&fn_entry->symbol_name));
- return;
- }
- case ConstPtrSpecialNull:
- buf_append_str(buf, "null");
- return;
- }
- zig_unreachable();
-}
-
-static void render_const_val_err_set(CodeGen *g, Buf *buf, ZigValue *const_val, ZigType *type_entry) {
- if (const_val->data.x_err_set == nullptr) {
- buf_append_str(buf, "null");
- } else {
- buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(&const_val->data.x_err_set->name));
- }
-}
-
-static void render_const_val_array(CodeGen *g, Buf *buf, Buf *type_name, ZigValue *const_val, uint64_t start, uint64_t len) {
- ConstArrayValue *array = &const_val->data.x_array;
- switch (array->special) {
- case ConstArraySpecialUndef:
- buf_append_str(buf, "undefined");
- return;
- case ConstArraySpecialBuf: {
- Buf *array_buf = array->data.s_buf;
- const char *base = &buf_ptr(array_buf)[start];
- assert(start + len <= buf_len(array_buf));
-
- buf_append_char(buf, '"');
- for (size_t i = 0; i < len; i += 1) {
- uint8_t c = base[i];
- if (c == '"') {
- buf_append_str(buf, "\\\"");
- } else {
- buf_append_char(buf, c);
- }
- }
- buf_append_char(buf, '"');
- return;
- }
- case ConstArraySpecialNone: {
- assert(start + len <= const_val->type->data.array.len);
- ZigValue *base = &array->data.s_none.elements[start];
- assert(len == 0 || base != nullptr);
-
- buf_appendf(buf, "%s{", buf_ptr(type_name));
- for (uint64_t i = 0; i < len; i += 1) {
- if (i != 0) buf_appendf(buf, ",");
- render_const_value(g, buf, &base[i]);
- }
- buf_appendf(buf, "}");
- return;
- }
- }
- zig_unreachable();
-}
-
-void render_const_value(CodeGen *g, Buf *buf, ZigValue *const_val) {
- if (const_val == nullptr) {
- buf_appendf(buf, "(invalid nullptr value)");
- return;
- }
- switch (const_val->special) {
- case ConstValSpecialRuntime:
- buf_appendf(buf, "(runtime value)");
- return;
- case ConstValSpecialLazy:
- buf_appendf(buf, "(lazy value)");
- return;
- case ConstValSpecialUndef:
- buf_appendf(buf, "undefined");
- return;
- case ConstValSpecialStatic:
- break;
- }
- assert(const_val->type);
-
- ZigType *type_entry = const_val->type;
- switch (type_entry->id) {
- case ZigTypeIdOpaque:
- zig_unreachable();
- case ZigTypeIdInvalid:
- buf_appendf(buf, "(invalid)");
- return;
- case ZigTypeIdVoid:
- buf_appendf(buf, "{}");
- return;
- case ZigTypeIdComptimeFloat:
- bigfloat_append_buf(buf, &const_val->data.x_bigfloat);
- return;
- case ZigTypeIdFloat:
- switch (type_entry->data.floating.bit_count) {
- case 16:
- buf_appendf(buf, "%f", zig_f16_to_double(const_val->data.x_f16));
- return;
- case 32:
- buf_appendf(buf, "%f", const_val->data.x_f32);
- return;
- case 64:
- buf_appendf(buf, "%f", const_val->data.x_f64);
- return;
- case 128:
- {
- const size_t extra_len = 100;
- size_t old_len = buf_len(buf);
- buf_resize(buf, old_len + extra_len);
- float64_t f64_value = f128M_to_f64(&const_val->data.x_f128);
- double double_value;
- memcpy(&double_value, &f64_value, sizeof(double));
- // TODO actual f128 printing to decimal
- int len = snprintf(buf_ptr(buf) + old_len, extra_len, "%f", double_value);
- assert(len > 0);
- buf_resize(buf, old_len + len);
- return;
- }
- default:
- zig_unreachable();
- }
- case ZigTypeIdComptimeInt:
- case ZigTypeIdInt:
- bigint_append_buf(buf, &const_val->data.x_bigint, 10);
- return;
- case ZigTypeIdEnumLiteral:
- buf_append_buf(buf, const_val->data.x_enum_literal);
- return;
- case ZigTypeIdMetaType:
- buf_appendf(buf, "%s", buf_ptr(&const_val->data.x_type->name));
- return;
- case ZigTypeIdUnreachable:
- buf_appendf(buf, "unreachable");
- return;
- case ZigTypeIdBool:
- {
- const char *value = const_val->data.x_bool ? "true" : "false";
- buf_appendf(buf, "%s", value);
- return;
- }
- case ZigTypeIdFn:
- {
- assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst);
- assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction);
- ZigFn *fn_entry = const_val->data.x_ptr.data.fn.fn_entry;
- buf_appendf(buf, "%s", buf_ptr(&fn_entry->symbol_name));
- return;
- }
- case ZigTypeIdPointer:
- return render_const_val_ptr(g, buf, const_val, type_entry);
- case ZigTypeIdArray: {
- uint64_t len = type_entry->data.array.len;
- render_const_val_array(g, buf, &type_entry->name, const_val, 0, len);
- return;
- }
- case ZigTypeIdVector: {
- uint32_t len = type_entry->data.vector.len;
- render_const_val_array(g, buf, &type_entry->name, const_val, 0, len);
- return;
- }
- case ZigTypeIdNull:
- {
- buf_appendf(buf, "null");
- return;
- }
- case ZigTypeIdUndefined:
- {
- buf_appendf(buf, "undefined");
- return;
- }
- case ZigTypeIdOptional:
- {
- if (get_src_ptr_type(const_val->type) != nullptr)
- return render_const_val_ptr(g, buf, const_val, type_entry->data.maybe.child_type);
- if (type_entry->data.maybe.child_type->id == ZigTypeIdErrorSet)
- return render_const_val_err_set(g, buf, const_val, type_entry->data.maybe.child_type);
- if (const_val->data.x_optional) {
- render_const_value(g, buf, const_val->data.x_optional);
- } else {
- buf_appendf(buf, "null");
- }
- return;
- }
- case ZigTypeIdBoundFn:
- {
- ZigFn *fn_entry = const_val->data.x_bound_fn.fn;
- buf_appendf(buf, "(bound fn %s)", buf_ptr(&fn_entry->symbol_name));
- return;
- }
- case ZigTypeIdStruct:
- {
- if (is_slice(type_entry)) {
- ZigValue *len_val = const_val->data.x_struct.fields[slice_len_index];
- size_t len = bigint_as_usize(&len_val->data.x_bigint);
-
- ZigValue *ptr_val = const_val->data.x_struct.fields[slice_ptr_index];
- if (ptr_val->special == ConstValSpecialUndef) {
- assert(len == 0);
- buf_appendf(buf, "((%s)(undefined))[0..0]", buf_ptr(&type_entry->name));
- return;
- }
- assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray);
- ZigValue *array = ptr_val->data.x_ptr.data.base_array.array_val;
- size_t start = ptr_val->data.x_ptr.data.base_array.elem_index;
-
- render_const_val_array(g, buf, &type_entry->name, array, start, len);
- } else {
- buf_appendf(buf, "(struct %s constant)", buf_ptr(&type_entry->name));
- }
- return;
- }
- case ZigTypeIdEnum:
- {
- TypeEnumField *field = find_enum_field_by_tag(type_entry, &const_val->data.x_enum_tag);
- if(field != nullptr){
- buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(field->name));
- } else {
- // untagged value in a non-exhaustive enum
- buf_appendf(buf, "%s.(", buf_ptr(&type_entry->name));
- bigint_append_buf(buf, &const_val->data.x_enum_tag, 10);
- buf_appendf(buf, ")");
- }
- return;
- }
- case ZigTypeIdErrorUnion:
- {
- buf_appendf(buf, "%s(", buf_ptr(&type_entry->name));
- ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set;
- if (err_set == nullptr) {
- render_const_value(g, buf, const_val->data.x_err_union.payload);
- } else {
- buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->data.error_union.err_set_type->name),
- buf_ptr(&err_set->name));
- }
- buf_appendf(buf, ")");
- return;
- }
- case ZigTypeIdUnion:
- {
- const BigInt *tag = &const_val->data.x_union.tag;
- TypeUnionField *field = find_union_field_by_tag(type_entry, tag);
- buf_appendf(buf, "%s { .%s = ", buf_ptr(&type_entry->name), buf_ptr(field->name));
- render_const_value(g, buf, const_val->data.x_union.payload);
- buf_append_str(buf, "}");
- return;
- }
- case ZigTypeIdErrorSet:
- return render_const_val_err_set(g, buf, const_val, type_entry);
- case ZigTypeIdFnFrame:
- buf_appendf(buf, "(TODO: async function frame value)");
- return;
-
- case ZigTypeIdAnyFrame:
- buf_appendf(buf, "(TODO: anyframe value)");
- return;
-
- }
- zig_unreachable();
-}
-
-ZigType *make_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) {
- assert(size_in_bits <= 65535);
- ZigType *entry = new_type_table_entry(ZigTypeIdInt);
-
- entry->size_in_bits = size_in_bits;
- if (size_in_bits != 0) {
- entry->llvm_type = LLVMIntType(size_in_bits);
- entry->abi_size = LLVMABISizeOfType(g->target_data_ref, entry->llvm_type);
- entry->abi_align = LLVMABIAlignmentOfType(g->target_data_ref, entry->llvm_type);
-
- if (size_in_bits >= 128 && entry->abi_align < 16) {
- // Override the incorrect alignment reported by LLVM. Clang does this as well.
- // On x86_64 there are some instructions like CMPXCHG16B which require this.
- // On all targets, integers 128 bits and above have ABI alignment of 16.
- // However for some targets, LLVM incorrectly reports this as 8.
- // See: https://github.com/ziglang/zig/issues/2987
- entry->abi_align = 16;
- }
- }
-
- const char u_or_i = is_signed ? 'i' : 'u';
- buf_resize(&entry->name, 0);
- buf_appendf(&entry->name, "%c%" PRIu32, u_or_i, size_in_bits);
-
- entry->data.integral.is_signed = is_signed;
- entry->data.integral.bit_count = size_in_bits;
- return entry;
-}
-
-uint32_t type_id_hash(TypeId x) {
- switch (x.id) {
- case ZigTypeIdInvalid:
- case ZigTypeIdOpaque:
- case ZigTypeIdMetaType:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdUnreachable:
- case ZigTypeIdFloat:
- case ZigTypeIdStruct:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdOptional:
- case ZigTypeIdErrorSet:
- case ZigTypeIdEnum:
- case ZigTypeIdUnion:
- case ZigTypeIdFn:
- case ZigTypeIdBoundFn:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- zig_unreachable();
- case ZigTypeIdErrorUnion:
- return hash_ptr(x.data.error_union.err_set_type) ^ hash_ptr(x.data.error_union.payload_type);
- case ZigTypeIdPointer:
- return hash_ptr(x.data.pointer.child_type) +
- (uint32_t)x.data.pointer.ptr_len * 1120226602u +
- (x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) +
- (x.data.pointer.is_volatile ? (uint32_t)536730450 : (uint32_t)1685612214) +
- (x.data.pointer.allow_zero ? (uint32_t)3324284834 : (uint32_t)3584904923) +
- (((uint32_t)x.data.pointer.alignment) ^ (uint32_t)0x777fbe0e) +
- (((uint32_t)x.data.pointer.bit_offset_in_host) ^ (uint32_t)2639019452) +
- (((uint32_t)x.data.pointer.vector_index) ^ (uint32_t)0x19199716) +
- (((uint32_t)x.data.pointer.host_int_bytes) ^ (uint32_t)529908881) *
- (x.data.pointer.sentinel ? hash_const_val(x.data.pointer.sentinel) : (uint32_t)2955491856);
- case ZigTypeIdArray:
- return hash_ptr(x.data.array.child_type) *
- ((uint32_t)x.data.array.size ^ (uint32_t)2122979968) *
- (x.data.array.sentinel ? hash_const_val(x.data.array.sentinel) : (uint32_t)1927201585);
- case ZigTypeIdInt:
- return (x.data.integer.is_signed ? (uint32_t)2652528194 : (uint32_t)163929201) +
- (((uint32_t)x.data.integer.bit_count) ^ (uint32_t)2998081557);
- case ZigTypeIdVector:
- return hash_ptr(x.data.vector.elem_type) * (x.data.vector.len * 526582681);
- }
- zig_unreachable();
-}
-
-bool type_id_eql(TypeId a, TypeId b) {
- if (a.id != b.id)
- return false;
- switch (a.id) {
- case ZigTypeIdInvalid:
- case ZigTypeIdMetaType:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdUnreachable:
- case ZigTypeIdFloat:
- case ZigTypeIdStruct:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdOptional:
- case ZigTypeIdErrorSet:
- case ZigTypeIdEnum:
- case ZigTypeIdUnion:
- case ZigTypeIdFn:
- case ZigTypeIdBoundFn:
- case ZigTypeIdOpaque:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- zig_unreachable();
- case ZigTypeIdErrorUnion:
- return a.data.error_union.err_set_type == b.data.error_union.err_set_type &&
- a.data.error_union.payload_type == b.data.error_union.payload_type;
-
- case ZigTypeIdPointer:
- return a.data.pointer.child_type == b.data.pointer.child_type &&
- a.data.pointer.ptr_len == b.data.pointer.ptr_len &&
- a.data.pointer.is_const == b.data.pointer.is_const &&
- a.data.pointer.is_volatile == b.data.pointer.is_volatile &&
- a.data.pointer.allow_zero == b.data.pointer.allow_zero &&
- a.data.pointer.alignment == b.data.pointer.alignment &&
- a.data.pointer.bit_offset_in_host == b.data.pointer.bit_offset_in_host &&
- a.data.pointer.vector_index == b.data.pointer.vector_index &&
- a.data.pointer.host_int_bytes == b.data.pointer.host_int_bytes &&
- (
- a.data.pointer.sentinel == b.data.pointer.sentinel ||
- (a.data.pointer.sentinel != nullptr && b.data.pointer.sentinel != nullptr &&
- const_values_equal(a.data.pointer.codegen, a.data.pointer.sentinel, b.data.pointer.sentinel))
- ) &&
- (
- a.data.pointer.inferred_struct_field == b.data.pointer.inferred_struct_field ||
- (a.data.pointer.inferred_struct_field != nullptr &&
- b.data.pointer.inferred_struct_field != nullptr &&
- a.data.pointer.inferred_struct_field->inferred_struct_type ==
- b.data.pointer.inferred_struct_field->inferred_struct_type &&
- buf_eql_buf(a.data.pointer.inferred_struct_field->field_name,
- b.data.pointer.inferred_struct_field->field_name))
- );
- case ZigTypeIdArray:
- return a.data.array.child_type == b.data.array.child_type &&
- a.data.array.size == b.data.array.size &&
- (
- a.data.array.sentinel == b.data.array.sentinel ||
- (a.data.array.sentinel != nullptr && b.data.array.sentinel != nullptr &&
- const_values_equal(a.data.array.codegen, a.data.array.sentinel, b.data.array.sentinel))
- );
- case ZigTypeIdInt:
- return a.data.integer.is_signed == b.data.integer.is_signed &&
- a.data.integer.bit_count == b.data.integer.bit_count;
- case ZigTypeIdVector:
- return a.data.vector.elem_type == b.data.vector.elem_type &&
- a.data.vector.len == b.data.vector.len;
- }
- zig_unreachable();
-}
-
-uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {
- switch (x.id) {
- case ZigLLVMFnIdCtz:
- return (uint32_t)(x.data.ctz.bit_count) * (uint32_t)810453934;
- case ZigLLVMFnIdClz:
- return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817;
- case ZigLLVMFnIdPopCount:
- return (uint32_t)(x.data.clz.bit_count) * (uint32_t)101195049;
- case ZigLLVMFnIdFloatOp:
- return (uint32_t)(x.data.floating.bit_count) * ((uint32_t)x.id + 1025) +
- (uint32_t)(x.data.floating.vector_len) * (((uint32_t)x.id << 5) + 1025) +
- (uint32_t)(x.data.floating.op) * (uint32_t)43789879;
- case ZigLLVMFnIdFMA:
- return (uint32_t)(x.data.floating.bit_count) * ((uint32_t)x.id + 1025) +
- (uint32_t)(x.data.floating.vector_len) * (((uint32_t)x.id << 5) + 1025);
- case ZigLLVMFnIdBswap:
- return (uint32_t)(x.data.bswap.bit_count) * ((uint32_t)3661994335) +
- (uint32_t)(x.data.bswap.vector_len) * (((uint32_t)x.id << 5) + 1025);
- case ZigLLVMFnIdBitReverse:
- return (uint32_t)(x.data.bit_reverse.bit_count) * (uint32_t)2621398431;
- case ZigLLVMFnIdOverflowArithmetic:
- return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) +
- ((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) +
- ((uint32_t)(x.data.overflow_arithmetic.is_signed) ? 1062315172 : 314955820) +
- x.data.overflow_arithmetic.vector_len * 1435156945;
- }
- zig_unreachable();
-}
-
-bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
- if (a.id != b.id)
- return false;
- switch (a.id) {
- case ZigLLVMFnIdCtz:
- return a.data.ctz.bit_count == b.data.ctz.bit_count;
- case ZigLLVMFnIdClz:
- return a.data.clz.bit_count == b.data.clz.bit_count;
- case ZigLLVMFnIdPopCount:
- return a.data.pop_count.bit_count == b.data.pop_count.bit_count;
- case ZigLLVMFnIdBswap:
- return a.data.bswap.bit_count == b.data.bswap.bit_count &&
- a.data.bswap.vector_len == b.data.bswap.vector_len;
- case ZigLLVMFnIdBitReverse:
- return a.data.bit_reverse.bit_count == b.data.bit_reverse.bit_count;
- case ZigLLVMFnIdFloatOp:
- return a.data.floating.bit_count == b.data.floating.bit_count &&
- a.data.floating.vector_len == b.data.floating.vector_len &&
- a.data.floating.op == b.data.floating.op;
- case ZigLLVMFnIdFMA:
- return a.data.floating.bit_count == b.data.floating.bit_count &&
- a.data.floating.vector_len == b.data.floating.vector_len;
- case ZigLLVMFnIdOverflowArithmetic:
- return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&
- (a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&
- (a.data.overflow_arithmetic.is_signed == b.data.overflow_arithmetic.is_signed) &&
- (a.data.overflow_arithmetic.vector_len == b.data.overflow_arithmetic.vector_len);
- }
- zig_unreachable();
-}
-
-static void init_const_undefined(CodeGen *g, ZigValue *const_val) {
- Error err;
- ZigType *wanted_type = const_val->type;
- if (wanted_type->id == ZigTypeIdArray) {
- const_val->special = ConstValSpecialStatic;
- const_val->data.x_array.special = ConstArraySpecialUndef;
- } else if (wanted_type->id == ZigTypeIdStruct) {
- if ((err = type_resolve(g, wanted_type, ResolveStatusZeroBitsKnown))) {
- return;
- }
-
- const_val->special = ConstValSpecialStatic;
- size_t field_count = wanted_type->data.structure.src_field_count;
- const_val->data.x_struct.fields = alloc_const_vals_ptrs(g, field_count);
- for (size_t i = 0; i < field_count; i += 1) {
- ZigValue *field_val = const_val->data.x_struct.fields[i];
- field_val->type = resolve_struct_field_type(g, wanted_type->data.structure.fields[i]);
- assert(field_val->type);
- init_const_undefined(g, field_val);
- field_val->parent.id = ConstParentIdStruct;
- field_val->parent.data.p_struct.struct_val = const_val;
- field_val->parent.data.p_struct.field_index = i;
- }
- } else {
- const_val->special = ConstValSpecialUndef;
- }
-}
-
-void expand_undef_struct(CodeGen *g, ZigValue *const_val) {
- if (const_val->special == ConstValSpecialUndef) {
- init_const_undefined(g, const_val);
- }
-}
-
-// Canonicalize the array value as ConstArraySpecialNone
-void expand_undef_array(CodeGen *g, ZigValue *const_val) {
- size_t elem_count;
- ZigType *elem_type;
- if (const_val->type->id == ZigTypeIdArray) {
- elem_count = const_val->type->data.array.len;
- elem_type = const_val->type->data.array.child_type;
- } else if (const_val->type->id == ZigTypeIdVector) {
- elem_count = const_val->type->data.vector.len;
- elem_type = const_val->type->data.vector.elem_type;
- } else {
- zig_unreachable();
- }
- if (const_val->special == ConstValSpecialUndef) {
- const_val->special = ConstValSpecialStatic;
- const_val->data.x_array.special = ConstArraySpecialUndef;
- }
- switch (const_val->data.x_array.special) {
- case ConstArraySpecialNone:
- return;
- case ConstArraySpecialUndef: {
- const_val->data.x_array.special = ConstArraySpecialNone;
- const_val->data.x_array.data.s_none.elements = g->pass1_arena->allocate(elem_count);
- for (size_t i = 0; i < elem_count; i += 1) {
- ZigValue *element_val = &const_val->data.x_array.data.s_none.elements[i];
- element_val->type = elem_type;
- init_const_undefined(g, element_val);
- element_val->parent.id = ConstParentIdArray;
- element_val->parent.data.p_array.array_val = const_val;
- element_val->parent.data.p_array.elem_index = i;
- }
- return;
- }
- case ConstArraySpecialBuf: {
- Buf *buf = const_val->data.x_array.data.s_buf;
- // If we're doing this it means that we are potentially modifying the data,
- // so we can't have it be in the string literals table
- g->string_literals_table.maybe_remove(buf);
-
- const_val->data.x_array.special = ConstArraySpecialNone;
- assert(elem_count == buf_len(buf));
- const_val->data.x_array.data.s_none.elements = g->pass1_arena->allocate(elem_count);
- for (size_t i = 0; i < elem_count; i += 1) {
- ZigValue *this_char = &const_val->data.x_array.data.s_none.elements[i];
- this_char->special = ConstValSpecialStatic;
- this_char->type = g->builtin_types.entry_u8;
- bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(buf)[i]);
- this_char->parent.id = ConstParentIdArray;
- this_char->parent.data.p_array.array_val = const_val;
- this_char->parent.data.p_array.elem_index = i;
- }
- return;
- }
- }
- zig_unreachable();
-}
-
-static const ZigTypeId all_type_ids[] = {
- ZigTypeIdMetaType,
- ZigTypeIdVoid,
- ZigTypeIdBool,
- ZigTypeIdUnreachable,
- ZigTypeIdInt,
- ZigTypeIdFloat,
- ZigTypeIdPointer,
- ZigTypeIdArray,
- ZigTypeIdStruct,
- ZigTypeIdComptimeFloat,
- ZigTypeIdComptimeInt,
- ZigTypeIdUndefined,
- ZigTypeIdNull,
- ZigTypeIdOptional,
- ZigTypeIdErrorUnion,
- ZigTypeIdErrorSet,
- ZigTypeIdEnum,
- ZigTypeIdUnion,
- ZigTypeIdFn,
- ZigTypeIdBoundFn,
- ZigTypeIdOpaque,
- ZigTypeIdFnFrame,
- ZigTypeIdAnyFrame,
- ZigTypeIdVector,
- ZigTypeIdEnumLiteral,
-};
-
-ZigTypeId type_id_at_index(size_t index) {
- assert(index < array_length(all_type_ids));
- return all_type_ids[index];
-}
-
-size_t type_id_len() {
- return array_length(all_type_ids);
-}
-
-size_t type_id_index(ZigType *entry) {
- switch (entry->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdMetaType:
- return 0;
- case ZigTypeIdVoid:
- return 1;
- case ZigTypeIdBool:
- return 2;
- case ZigTypeIdUnreachable:
- return 3;
- case ZigTypeIdInt:
- return 4;
- case ZigTypeIdFloat:
- return 5;
- case ZigTypeIdPointer:
- return 6;
- case ZigTypeIdArray:
- return 7;
- case ZigTypeIdStruct:
- if (entry->data.structure.special == StructSpecialSlice)
- return 6;
- return 8;
- case ZigTypeIdComptimeFloat:
- return 9;
- case ZigTypeIdComptimeInt:
- return 10;
- case ZigTypeIdUndefined:
- return 11;
- case ZigTypeIdNull:
- return 12;
- case ZigTypeIdOptional:
- return 13;
- case ZigTypeIdErrorUnion:
- return 14;
- case ZigTypeIdErrorSet:
- return 15;
- case ZigTypeIdEnum:
- return 16;
- case ZigTypeIdUnion:
- return 17;
- case ZigTypeIdFn:
- return 18;
- case ZigTypeIdBoundFn:
- return 19;
- case ZigTypeIdOpaque:
- return 20;
- case ZigTypeIdFnFrame:
- return 21;
- case ZigTypeIdAnyFrame:
- return 22;
- case ZigTypeIdVector:
- return 23;
- case ZigTypeIdEnumLiteral:
- return 24;
- }
- zig_unreachable();
-}
-
-const char *type_id_name(ZigTypeId id) {
- switch (id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdMetaType:
- return "Type";
- case ZigTypeIdVoid:
- return "Void";
- case ZigTypeIdBool:
- return "Bool";
- case ZigTypeIdUnreachable:
- return "NoReturn";
- case ZigTypeIdInt:
- return "Int";
- case ZigTypeIdFloat:
- return "Float";
- case ZigTypeIdPointer:
- return "Pointer";
- case ZigTypeIdArray:
- return "Array";
- case ZigTypeIdStruct:
- return "Struct";
- case ZigTypeIdComptimeFloat:
- return "ComptimeFloat";
- case ZigTypeIdComptimeInt:
- return "ComptimeInt";
- case ZigTypeIdEnumLiteral:
- return "EnumLiteral";
- case ZigTypeIdUndefined:
- return "Undefined";
- case ZigTypeIdNull:
- return "Null";
- case ZigTypeIdOptional:
- return "Optional";
- case ZigTypeIdErrorUnion:
- return "ErrorUnion";
- case ZigTypeIdErrorSet:
- return "ErrorSet";
- case ZigTypeIdEnum:
- return "Enum";
- case ZigTypeIdUnion:
- return "Union";
- case ZigTypeIdFn:
- return "Fn";
- case ZigTypeIdBoundFn:
- return "BoundFn";
- case ZigTypeIdOpaque:
- return "Opaque";
- case ZigTypeIdVector:
- return "Vector";
- case ZigTypeIdFnFrame:
- return "Frame";
- case ZigTypeIdAnyFrame:
- return "AnyFrame";
- }
- 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.
- g->align_amt_type = get_int_type(g, false, 29);
- }
- return g->align_amt_type;
-}
-
-uint32_t type_ptr_hash(const ZigType *ptr) {
- return hash_ptr((void*)ptr);
-}
-
-bool type_ptr_eql(const ZigType *a, const ZigType *b) {
- return a == b;
-}
-
-uint32_t pkg_ptr_hash(const ZigPackage *ptr) {
- return hash_ptr((void*)ptr);
-}
-
-bool pkg_ptr_eql(const ZigPackage *a, const ZigPackage *b) {
- return a == b;
-}
-
-uint32_t tld_ptr_hash(const Tld *ptr) {
- return hash_ptr((void*)ptr);
-}
-
-bool tld_ptr_eql(const Tld *a, const Tld *b) {
- return a == b;
-}
-
-uint32_t node_ptr_hash(const AstNode *ptr) {
- return hash_ptr((void*)ptr);
-}
-
-bool node_ptr_eql(const AstNode *a, const AstNode *b) {
- return a == b;
-}
-
-uint32_t fn_ptr_hash(const ZigFn *ptr) {
- return hash_ptr((void*)ptr);
-}
-
-bool fn_ptr_eql(const ZigFn *a, const ZigFn *b) {
- return a == b;
-}
-
-uint32_t err_ptr_hash(const ErrorTableEntry *ptr) {
- return hash_ptr((void*)ptr);
-}
-
-bool err_ptr_eql(const ErrorTableEntry *a, const ErrorTableEntry *b) {
- return a == b;
-}
-
-ZigValue *get_builtin_value(CodeGen *codegen, const char *name) {
- ScopeDecls *builtin_scope = get_container_scope(codegen->compile_var_import);
- Tld *tld = find_container_decl(codegen, builtin_scope, buf_create_from_str(name));
- assert(tld != nullptr);
- resolve_top_level_decl(codegen, tld, nullptr, false);
- assert(tld->id == TldIdVar && tld->resolution == TldResolutionOk);
- TldVar *tld_var = (TldVar *)tld;
- ZigValue *var_value = tld_var->var->const_value;
- assert(var_value != nullptr);
- return var_value;
-}
-
-ZigType *get_builtin_type(CodeGen *codegen, const char *name) {
- ZigValue *type_val = get_builtin_value(codegen, name);
- assert(type_val->type->id == ZigTypeIdMetaType);
- return type_val->data.x_type;
-}
-
-bool type_is_global_error_set(ZigType *err_set_type) {
- assert(err_set_type->id == ZigTypeIdErrorSet);
- assert(!err_set_type->data.error_set.incomplete);
- return err_set_type->data.error_set.err_count == UINT32_MAX;
-}
-
-bool type_can_fail(ZigType *type_entry) {
- return type_entry->id == ZigTypeIdErrorUnion || type_entry->id == ZigTypeIdErrorSet;
-}
-
-bool fn_type_can_fail(FnTypeId *fn_type_id) {
- return type_can_fail(fn_type_id->return_type);
-}
-
-// ErrorNone - result pointer has the type
-// ErrorOverflow - an integer primitive type has too large a bit width
-// ErrorPrimitiveTypeNotFound - result pointer unchanged
-Error get_primitive_type(CodeGen *g, Buf *name, ZigType **result) {
- if (buf_len(name) >= 2) {
- uint8_t first_c = buf_ptr(name)[0];
- if (first_c == 'i' || first_c == 'u') {
- for (size_t i = 1; i < buf_len(name); i += 1) {
- uint8_t c = buf_ptr(name)[i];
- if (c < '0' || c > '9') {
- goto not_integer;
- }
- }
- bool is_signed = (first_c == 'i');
- unsigned long int bit_count = strtoul(buf_ptr(name) + 1, nullptr, 10);
- // strtoul returns ULONG_MAX on errors, so this comparison catches that as well.
- if (bit_count >= 65536) return ErrorOverflow;
- *result = get_int_type(g, is_signed, bit_count);
- return ErrorNone;
- }
- }
-
-not_integer:
-
- auto primitive_table_entry = g->primitive_type_table.maybe_get(name);
- if (primitive_table_entry == nullptr)
- return ErrorPrimitiveTypeNotFound;
-
- *result = primitive_table_entry->value;
- 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);
- }
-}
-
-static X64CABIClass type_windows_abi_x86_64_class(CodeGen *g, ZigType *ty, size_t ty_size) {
- // https://docs.microsoft.com/en-gb/cpp/build/x64-calling-convention?view=vs-2017
- switch (ty->id) {
- case ZigTypeIdEnum:
- case ZigTypeIdInt:
- case ZigTypeIdBool:
- return X64CABIClass_INTEGER;
- case ZigTypeIdFloat:
- case ZigTypeIdVector:
- return X64CABIClass_SSE;
- case ZigTypeIdStruct:
- case ZigTypeIdUnion: {
- if (ty_size <= 8)
- return X64CABIClass_INTEGER;
- return X64CABIClass_MEMORY;
- }
- default:
- return X64CABIClass_Unknown;
- }
-}
-
-static X64CABIClass type_system_V_abi_x86_64_class(CodeGen *g, ZigType *ty, size_t ty_size) {
- switch (ty->id) {
- case ZigTypeIdEnum:
- case ZigTypeIdInt:
- case ZigTypeIdBool:
- return X64CABIClass_INTEGER;
- case ZigTypeIdFloat:
- case ZigTypeIdVector:
- return X64CABIClass_SSE;
- case ZigTypeIdStruct: {
- // "If the size of an object is larger than four eightbytes, or it contains unaligned
- // fields, it has class MEMORY"
- if (ty_size > 32)
- return X64CABIClass_MEMORY;
- if (ty->data.structure.layout != ContainerLayoutExtern) {
- // TODO determine whether packed structs have any unaligned fields
- return X64CABIClass_Unknown;
- }
- // "If the size of the aggregate exceeds two eightbytes and the first eight-
- // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument
- // is passed in memory."
- if (ty_size > 16) {
- // Zig doesn't support vectors and large fp registers yet, so this will always
- // be memory.
- return X64CABIClass_MEMORY;
- }
- X64CABIClass working_class = X64CABIClass_Unknown;
- for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) {
- X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.structure.fields[0]->type_entry);
- if (field_class == X64CABIClass_Unknown)
- return X64CABIClass_Unknown;
- if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) {
- working_class = field_class;
- }
- }
- return working_class;
- }
- case ZigTypeIdUnion: {
- // "If the size of an object is larger than four eightbytes, or it contains unaligned
- // fields, it has class MEMORY"
- if (ty_size > 32)
- return X64CABIClass_MEMORY;
- if (ty->data.unionation.layout != ContainerLayoutExtern)
- return X64CABIClass_MEMORY;
- // "If the size of the aggregate exceeds two eightbytes and the first eight-
- // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument
- // is passed in memory."
- if (ty_size > 16) {
- // Zig doesn't support vectors and large fp registers yet, so this will always
- // be memory.
- return X64CABIClass_MEMORY;
- }
- X64CABIClass working_class = X64CABIClass_Unknown;
- for (uint32_t i = 0; i < ty->data.unionation.src_field_count; i += 1) {
- X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.unionation.fields->type_entry);
- if (field_class == X64CABIClass_Unknown)
- return X64CABIClass_Unknown;
- if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) {
- working_class = field_class;
- }
- }
- return working_class;
- }
- default:
- return X64CABIClass_Unknown;
- }
-}
-
-X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) {
- Error err;
-
- const size_t ty_size = type_size(g, ty);
- ZigType *ptr_type;
- if ((err = get_codegen_ptr_type(g, ty, &ptr_type))) return X64CABIClass_Unknown;
- if (ptr_type != nullptr)
- return X64CABIClass_INTEGER;
-
- if (g->zig_target->os == OsWindows || g->zig_target->os == OsUefi) {
- return type_windows_abi_x86_64_class(g, ty, ty_size);
- } else if (g->zig_target->arch == ZigLLVM_aarch64 ||
- g->zig_target->arch == ZigLLVM_aarch64_be)
- {
- X64CABIClass result = type_system_V_abi_x86_64_class(g, ty, ty_size);
- return (result == X64CABIClass_MEMORY) ? X64CABIClass_MEMORY_nobyval : result;
- } else {
- return type_system_V_abi_x86_64_class(g, ty, ty_size);
- }
-}
-
-// NOTE this does not depend on x86_64
-Error type_is_c_abi_int(CodeGen *g, ZigType *ty, bool *result) {
- if (ty->id == ZigTypeIdInt ||
- ty->id == ZigTypeIdFloat ||
- ty->id == ZigTypeIdBool ||
- ty->id == ZigTypeIdEnum ||
- ty->id == ZigTypeIdVoid ||
- ty->id == ZigTypeIdUnreachable)
- {
- *result = true;
- return ErrorNone;
- }
-
- Error err;
- ZigType *ptr_type;
- if ((err = get_codegen_ptr_type(g, ty, &ptr_type))) return err;
- *result = ptr_type != nullptr;
- return ErrorNone;
-}
-
-bool type_is_c_abi_int_bail(CodeGen *g, ZigType *ty) {
- Error err;
- bool result;
- if ((err = type_is_c_abi_int(g, ty, &result)))
- codegen_report_errors_and_exit(g);
-
- return result;
-}
-
-uint32_t get_host_int_bytes(CodeGen *g, ZigType *struct_type, TypeStructField *field) {
- assert(struct_type->id == ZigTypeIdStruct);
- if (struct_type->data.structure.layout != ContainerLayoutAuto) {
- assert(type_is_resolved(struct_type, ResolveStatusSizeKnown));
- }
- if (struct_type->data.structure.host_int_bytes == nullptr)
- return 0;
- return struct_type->data.structure.host_int_bytes[field->gen_index];
-}
-
-Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node,
- ZigValue *const_val, ZigType *wanted_type)
-{
- ZigValue ptr_val = {};
- ptr_val.special = ConstValSpecialStatic;
- ptr_val.type = get_pointer_to_type(codegen, wanted_type, true);
- ptr_val.data.x_ptr.mut = ConstPtrMutComptimeConst;
- ptr_val.data.x_ptr.special = ConstPtrSpecialRef;
- ptr_val.data.x_ptr.data.ref.pointee = const_val;
- if (const_ptr_pointee(ira, codegen, &ptr_val, source_node) == nullptr)
- return ErrorSemanticAnalyzeFail;
-
- return ErrorNone;
-}
-
-const char *container_string(ContainerKind kind) {
- switch (kind) {
- case ContainerKindEnum: return "enum";
- case ContainerKindStruct: return "struct";
- case ContainerKindUnion: return "union";
- }
- zig_unreachable();
-}
-
-bool ptr_allows_addr_zero(ZigType *ptr_type) {
- if (ptr_type->id == ZigTypeIdPointer) {
- return ptr_type->data.pointer.allow_zero;
- } else if (ptr_type->id == ZigTypeIdOptional) {
- return true;
- }
- return false;
-}
-
-Buf *type_bare_name(ZigType *type_entry) {
- if (is_slice(type_entry)) {
- return &type_entry->name;
- } else if (is_container(type_entry)) {
- return get_container_scope(type_entry)->bare_name;
- } else if (type_entry->id == ZigTypeIdOpaque) {
- return type_entry->data.opaque.bare_name;
- } else {
- return &type_entry->name;
- }
-}
-
-// TODO this will have to be more clever, probably using the full name
-// and replacing '.' with '_' or something like that
-Buf *type_h_name(ZigType *t) {
- return type_bare_name(t);
-}
-
-static void resolve_llvm_types_slice(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) {
- if (type->data.structure.resolve_status >= wanted_resolve_status) return;
-
- ZigType *ptr_type = type->data.structure.fields[slice_ptr_index]->type_entry;
- ZigType *child_type = ptr_type->data.pointer.child_type;
- ZigType *usize_type = g->builtin_types.entry_usize;
-
- bool done = false;
- if (ptr_type->data.pointer.is_const || ptr_type->data.pointer.is_volatile ||
- ptr_type->data.pointer.explicit_alignment != 0 || ptr_type->data.pointer.allow_zero ||
- ptr_type->data.pointer.sentinel != nullptr)
- {
- ZigType *peer_ptr_type = get_pointer_to_type_extra(g, child_type, false, false,
- PtrLenUnknown, 0, 0, 0, false);
- ZigType *peer_slice_type = get_slice_type(g, peer_ptr_type);
-
- assertNoError(type_resolve(g, peer_slice_type, wanted_resolve_status));
- type->llvm_type = peer_slice_type->llvm_type;
- type->llvm_di_type = peer_slice_type->llvm_di_type;
- type->data.structure.resolve_status = peer_slice_type->data.structure.resolve_status;
- done = true;
- }
-
- // If the child type is []const T then we need to make sure the type ref
- // and debug info is the same as if the child type were []T.
- if (is_slice(child_type)) {
- ZigType *child_ptr_type = child_type->data.structure.fields[slice_ptr_index]->type_entry;
- assert(child_ptr_type->id == ZigTypeIdPointer);
- if (child_ptr_type->data.pointer.is_const || child_ptr_type->data.pointer.is_volatile ||
- child_ptr_type->data.pointer.explicit_alignment != 0 || child_ptr_type->data.pointer.allow_zero ||
- child_ptr_type->data.pointer.sentinel != nullptr)
- {
- ZigType *grand_child_type = child_ptr_type->data.pointer.child_type;
- ZigType *bland_child_ptr_type = get_pointer_to_type_extra(g, grand_child_type, false, false,
- PtrLenUnknown, 0, 0, 0, false);
- ZigType *bland_child_slice = get_slice_type(g, bland_child_ptr_type);
- ZigType *peer_ptr_type = get_pointer_to_type_extra(g, bland_child_slice, false, false,
- PtrLenUnknown, 0, 0, 0, false);
- ZigType *peer_slice_type = get_slice_type(g, peer_ptr_type);
-
- assertNoError(type_resolve(g, peer_slice_type, wanted_resolve_status));
- type->llvm_type = peer_slice_type->llvm_type;
- type->llvm_di_type = peer_slice_type->llvm_di_type;
- type->data.structure.resolve_status = peer_slice_type->data.structure.resolve_status;
- done = true;
- }
- }
-
- if (done) return;
-
- LLVMTypeRef usize_llvm_type = get_llvm_type(g, usize_type);
- ZigLLVMDIType *usize_llvm_di_type = get_llvm_di_type(g, usize_type);
- ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
- ZigLLVMDIFile *di_file = nullptr;
- unsigned line = 0;
-
- if (type->data.structure.resolve_status < ResolveStatusLLVMFwdDecl) {
- type->llvm_type = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&type->name));
-
- type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
- ZigLLVMTag_DW_structure_type(), buf_ptr(&type->name),
- compile_unit_scope, di_file, line);
-
- type->data.structure.resolve_status = ResolveStatusLLVMFwdDecl;
- if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) return;
- }
-
- if (!type_has_bits(g, child_type)) {
- LLVMTypeRef element_types[] = {
- usize_llvm_type,
- };
- LLVMStructSetBody(type->llvm_type, element_types, 1, false);
-
- uint64_t len_debug_size_in_bits = usize_type->size_in_bits;
- uint64_t len_debug_align_in_bits = 8*usize_type->abi_align;
- uint64_t len_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 0);
-
- uint64_t debug_size_in_bits = type->size_in_bits;
- uint64_t debug_align_in_bits = 8*type->abi_align;
-
- ZigLLVMDIType *di_element_types[] = {
- ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type),
- "len", di_file, line,
- len_debug_size_in_bits,
- len_debug_align_in_bits,
- len_offset_in_bits,
- ZigLLVM_DIFlags_Zero,
- usize_llvm_di_type),
- };
- ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
- compile_unit_scope,
- buf_ptr(&type->name),
- di_file, line, debug_size_in_bits, debug_align_in_bits,
- ZigLLVM_DIFlags_Zero,
- nullptr, di_element_types, 1, 0, nullptr, "");
-
- ZigLLVMReplaceTemporary(g->dbuilder, type->llvm_di_type, replacement_di_type);
- type->llvm_di_type = replacement_di_type;
- type->data.structure.resolve_status = ResolveStatusLLVMFull;
- return;
- }
-
- LLVMTypeRef element_types[2];
- element_types[slice_ptr_index] = get_llvm_type(g, ptr_type);
- element_types[slice_len_index] = get_llvm_type(g, g->builtin_types.entry_usize);
- if (type->data.structure.resolve_status >= wanted_resolve_status) return;
- LLVMStructSetBody(type->llvm_type, element_types, 2, false);
-
- uint64_t ptr_debug_size_in_bits = ptr_type->size_in_bits;
- uint64_t ptr_debug_align_in_bits = 8*ptr_type->abi_align;
- uint64_t ptr_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 0);
-
- uint64_t len_debug_size_in_bits = usize_type->size_in_bits;
- uint64_t len_debug_align_in_bits = 8*usize_type->abi_align;
- uint64_t len_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 1);
-
- uint64_t debug_size_in_bits = type->size_in_bits;
- uint64_t debug_align_in_bits = 8*type->abi_align;
-
- ZigLLVMDIType *di_element_types[] = {
- ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type),
- "ptr", di_file, line,
- ptr_debug_size_in_bits,
- ptr_debug_align_in_bits,
- ptr_offset_in_bits,
- ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, ptr_type)),
- ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type),
- "len", di_file, line,
- len_debug_size_in_bits,
- len_debug_align_in_bits,
- len_offset_in_bits,
- ZigLLVM_DIFlags_Zero, usize_llvm_di_type),
- };
- ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
- compile_unit_scope,
- buf_ptr(&type->name),
- di_file, line, debug_size_in_bits, debug_align_in_bits,
- ZigLLVM_DIFlags_Zero,
- nullptr, di_element_types, 2, 0, nullptr, "");
-
- ZigLLVMReplaceTemporary(g->dbuilder, type->llvm_di_type, replacement_di_type);
- type->llvm_di_type = replacement_di_type;
- type->data.structure.resolve_status = ResolveStatusLLVMFull;
-}
-
-static LLVMTypeRef get_llvm_type_of_n_bytes(unsigned byte_size) {
- return byte_size == 1 ?
- LLVMInt8Type() : LLVMArrayType(LLVMInt8Type(), byte_size);
-}
-
-static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveStatus wanted_resolve_status,
- ZigType *async_frame_type)
-{
- assert(struct_type->id == ZigTypeIdStruct);
- assert(struct_type->data.structure.resolve_status != ResolveStatusInvalid);
- assert(struct_type->data.structure.resolve_status >= ResolveStatusSizeKnown);
- assert(struct_type->data.structure.fields || struct_type->data.structure.src_field_count == 0);
- if (struct_type->data.structure.resolve_status >= wanted_resolve_status) return;
-
- AstNode *decl_node = struct_type->data.structure.decl_node;
- ZigLLVMDIFile *di_file;
- ZigLLVMDIScope *di_scope;
- unsigned line;
- if (decl_node != nullptr) {
- Scope *scope = &struct_type->data.structure.decls_scope->base;
- ZigType *import = get_scope_import(scope);
- di_file = import->data.structure.root_struct->di_file;
- di_scope = ZigLLVMFileToScope(di_file);
- line = decl_node->line + 1;
- } else {
- di_file = nullptr;
- di_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
- line = 0;
- }
-
- if (struct_type->data.structure.resolve_status < ResolveStatusLLVMFwdDecl) {
- struct_type->llvm_type = type_has_bits(g, struct_type) ?
- LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&struct_type->name)) : LLVMVoidType();
- unsigned dwarf_kind = ZigLLVMTag_DW_structure_type();
- struct_type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
- dwarf_kind, buf_ptr(&struct_type->name),
- di_scope, di_file, line);
-
- struct_type->data.structure.resolve_status = ResolveStatusLLVMFwdDecl;
- if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) {
- struct_type->data.structure.llvm_full_type_queue_index = g->type_resolve_stack.length;
- g->type_resolve_stack.append(struct_type);
- return;
- } else {
- struct_type->data.structure.llvm_full_type_queue_index = SIZE_MAX;
- }
- }
-
- size_t field_count = struct_type->data.structure.src_field_count;
- // Every field could potentially have a generated padding field after it.
- LLVMTypeRef *element_types = heap::c_allocator.allocate(field_count * 2);
-
- bool packed = (struct_type->data.structure.layout == ContainerLayoutPacked);
- size_t packed_bits_offset = 0;
- size_t first_packed_bits_offset_misalign = SIZE_MAX;
- size_t debug_field_count = 0;
-
- // trigger all the recursive get_llvm_type calls
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *field = struct_type->data.structure.fields[i];
- ZigType *field_type = field->type_entry;
- if (!type_has_bits(g, field_type))
- continue;
- (void)get_llvm_type(g, field_type);
- if (struct_type->data.structure.resolve_status >= wanted_resolve_status) return;
- }
-
- size_t gen_field_index = 0;
-
- // Calculate what LLVM thinks the ABI align of the struct will be. We do this to avoid
- // inserting padding bytes where LLVM would do it automatically.
- size_t llvm_struct_abi_align = 0;
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *field = struct_type->data.structure.fields[i];
- ZigType *field_type = field->type_entry;
- if (field->is_comptime || !type_has_bits(g, field_type))
- continue;
- LLVMTypeRef field_llvm_type = get_llvm_type(g, field_type);
- size_t llvm_field_abi_align = LLVMABIAlignmentOfType(g->target_data_ref, field_llvm_type);
- llvm_struct_abi_align = max(llvm_struct_abi_align, llvm_field_abi_align);
- }
-
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *field = struct_type->data.structure.fields[i];
- ZigType *field_type = field->type_entry;
-
- if (field->is_comptime || !type_has_bits(g, field_type)) {
- field->gen_index = SIZE_MAX;
- continue;
- }
-
- if (packed) {
- size_t field_size_in_bits = type_size_bits(g, field_type);
- size_t next_packed_bits_offset = packed_bits_offset + field_size_in_bits;
-
- if (first_packed_bits_offset_misalign != SIZE_MAX) {
- // this field is not byte-aligned; it is part of the previous field with a bit offset
-
- size_t full_bit_count = next_packed_bits_offset - first_packed_bits_offset_misalign;
- size_t full_abi_size = get_abi_size_bytes(full_bit_count, g->pointer_size_bytes);
- if (full_abi_size * 8 == full_bit_count) {
- // next field recovers ABI alignment
- element_types[gen_field_index] = get_llvm_type_of_n_bytes(full_abi_size);
- gen_field_index += 1;
- first_packed_bits_offset_misalign = SIZE_MAX;
- }
- } else if (get_abi_size_bytes(field_type->size_in_bits, g->pointer_size_bytes) * 8 != field_size_in_bits) {
- first_packed_bits_offset_misalign = packed_bits_offset;
- } else {
- // This is a byte-aligned field (both start and end) in a packed struct.
- element_types[gen_field_index] = get_llvm_type(g, field_type);
- assert(get_abi_size_bytes(field_type->size_in_bits, g->pointer_size_bytes) ==
- LLVMStoreSizeOfType(g->target_data_ref, element_types[gen_field_index]));
- gen_field_index += 1;
- }
- packed_bits_offset = next_packed_bits_offset;
- } else {
- LLVMTypeRef llvm_type;
- if (i == 0 && async_frame_type != nullptr) {
- assert(async_frame_type->id == ZigTypeIdFnFrame);
- assert(field_type->id == ZigTypeIdFn);
- resolve_llvm_types_fn(g, async_frame_type->data.frame.fn);
- llvm_type = LLVMPointerType(async_frame_type->data.frame.fn->raw_type_ref, 0);
- } else {
- llvm_type = get_llvm_type(g, field_type);
- }
- element_types[gen_field_index] = llvm_type;
- field->gen_index = gen_field_index;
- gen_field_index += 1;
-
- // find the next non-zero-byte field for offset calculations
- size_t next_src_field_index = i + 1;
- for (; next_src_field_index < field_count; next_src_field_index += 1) {
- if (type_has_bits(g, struct_type->data.structure.fields[next_src_field_index]->type_entry))
- break;
- }
- size_t next_abi_align;
- if (next_src_field_index == field_count) {
- next_abi_align = struct_type->abi_align;
- } else {
- if (struct_type->data.structure.fields[next_src_field_index]->align == 0) {
- next_abi_align = struct_type->data.structure.fields[next_src_field_index]->type_entry->abi_align;
- } else {
- next_abi_align = struct_type->data.structure.fields[next_src_field_index]->align;
- }
- }
- size_t llvm_next_abi_align = (next_src_field_index == field_count) ?
- llvm_struct_abi_align :
- LLVMABIAlignmentOfType(g->target_data_ref,
- get_llvm_type(g, struct_type->data.structure.fields[next_src_field_index]->type_entry));
-
- size_t next_offset = next_field_offset(field->offset, struct_type->abi_align,
- field_type->abi_size, next_abi_align);
- size_t llvm_next_offset = next_field_offset(field->offset, llvm_struct_abi_align,
- LLVMABISizeOfType(g->target_data_ref, llvm_type), llvm_next_abi_align);
-
- assert(next_offset >= llvm_next_offset);
- if (next_offset > llvm_next_offset) {
- size_t pad_bytes = next_offset - (field->offset + LLVMStoreSizeOfType(g->target_data_ref, llvm_type));
- if (pad_bytes != 0) {
- LLVMTypeRef pad_llvm_type = LLVMArrayType(LLVMInt8Type(), pad_bytes);
- element_types[gen_field_index] = pad_llvm_type;
- gen_field_index += 1;
- }
- }
- }
- debug_field_count += 1;
- }
- if (!packed) {
- struct_type->data.structure.gen_field_count = gen_field_index;
- }
-
- if (first_packed_bits_offset_misalign != SIZE_MAX) {
- size_t full_bit_count = packed_bits_offset - first_packed_bits_offset_misalign;
- size_t full_abi_size = get_abi_size_bytes(full_bit_count, g->pointer_size_bytes);
- element_types[gen_field_index] = get_llvm_type_of_n_bytes(full_abi_size);
- gen_field_index += 1;
- }
-
- if (type_has_bits(g, struct_type)) {
- assert(struct_type->data.structure.gen_field_count == gen_field_index);
- LLVMStructSetBody(struct_type->llvm_type, element_types,
- (unsigned)struct_type->data.structure.gen_field_count, packed);
- }
-
- ZigLLVMDIType **di_element_types = heap::c_allocator.allocate(debug_field_count);
- size_t debug_field_index = 0;
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *field = struct_type->data.structure.fields[i];
- //fprintf(stderr, "%s at gen index %zu\n", buf_ptr(field->name), field->gen_index);
-
- size_t gen_field_index = field->gen_index;
- if (gen_field_index == SIZE_MAX) {
- continue;
- }
-
- ZigType *field_type = field->type_entry;
-
- // if the field is a function, actually the debug info should be a pointer.
- ZigLLVMDIType *field_di_type;
- if (field_type->id == ZigTypeIdFn) {
- ZigType *field_ptr_type = get_pointer_to_type(g, field_type, true);
- uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, get_llvm_type(g, field_ptr_type));
- uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, get_llvm_type(g, field_ptr_type));
- field_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, get_llvm_di_type(g, field_type),
- debug_size_in_bits, debug_align_in_bits, buf_ptr(&field_ptr_type->name));
- } else {
- field_di_type = get_llvm_di_type(g, field_type);
- }
-
- uint64_t debug_size_in_bits;
- uint64_t debug_align_in_bits;
- uint64_t debug_offset_in_bits;
- if (packed) {
- debug_size_in_bits = field->type_entry->size_in_bits;
- debug_align_in_bits = 8 * field->type_entry->abi_align;
- debug_offset_in_bits = 8 * field->offset + field->bit_offset_in_host;
- } else {
- debug_size_in_bits = 8 * get_store_size_bytes(field_type->size_in_bits);
- debug_align_in_bits = 8 * field_type->abi_align;
- debug_offset_in_bits = 8 * field->offset;
- }
- unsigned line;
- if (decl_node != nullptr) {
- AstNode *field_node = field->decl_node;
- line = field_node->line + 1;
- } else {
- line = 0;
- }
- di_element_types[debug_field_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(struct_type->llvm_di_type), buf_ptr(field->name),
- di_file, line,
- debug_size_in_bits,
- debug_align_in_bits,
- debug_offset_in_bits,
- ZigLLVM_DIFlags_Zero, field_di_type);
- assert(di_element_types[debug_field_index]);
- debug_field_index += 1;
- }
-
- uint64_t debug_size_in_bits = 8*get_store_size_bytes(struct_type->size_in_bits);
- uint64_t debug_align_in_bits = 8*struct_type->abi_align;
- ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
- di_scope,
- buf_ptr(&struct_type->name),
- di_file, line,
- debug_size_in_bits,
- debug_align_in_bits,
- ZigLLVM_DIFlags_Zero,
- nullptr, di_element_types, (int)debug_field_count, 0, nullptr, "");
-
- ZigLLVMReplaceTemporary(g->dbuilder, struct_type->llvm_di_type, replacement_di_type);
- struct_type->llvm_di_type = replacement_di_type;
- struct_type->data.structure.resolve_status = ResolveStatusLLVMFull;
- if (struct_type->data.structure.llvm_full_type_queue_index != SIZE_MAX) {
- ZigType *last = g->type_resolve_stack.last();
- assert(last->id == ZigTypeIdStruct);
- last->data.structure.llvm_full_type_queue_index = struct_type->data.structure.llvm_full_type_queue_index;
- g->type_resolve_stack.swap_remove(struct_type->data.structure.llvm_full_type_queue_index);
- struct_type->data.structure.llvm_full_type_queue_index = SIZE_MAX;
- }
-}
-
-// This is to be used instead of void for debug info types, to avoid tripping
-// Assertion `!isa(Scope) && "shouldn't make a namespace scope for a type"'
-// when targeting CodeView (Windows).
-static ZigLLVMDIType *make_empty_namespace_llvm_di_type(CodeGen *g, ZigType *import, const char *name,
- AstNode *decl_node)
-{
- uint64_t debug_size_in_bits = 0;
- uint64_t debug_align_in_bits = 0;
- ZigLLVMDIType **di_element_types = nullptr;
- size_t debug_field_count = 0;
- return ZigLLVMCreateDebugStructType(g->dbuilder,
- ZigLLVMFileToScope(import->data.structure.root_struct->di_file),
- name,
- import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
- debug_size_in_bits,
- debug_align_in_bits,
- ZigLLVM_DIFlags_Zero,
- nullptr, di_element_types, (int)debug_field_count, 0, nullptr, "");
-}
-
-static void resolve_llvm_types_enum(CodeGen *g, ZigType *enum_type, ResolveStatus wanted_resolve_status) {
- assert(enum_type->data.enumeration.resolve_status >= ResolveStatusSizeKnown);
- if (enum_type->data.enumeration.resolve_status >= wanted_resolve_status) return;
-
- Scope *scope = &enum_type->data.enumeration.decls_scope->base;
- ZigType *import = get_scope_import(scope);
- AstNode *decl_node = enum_type->data.enumeration.decl_node;
-
- if (!type_has_bits(g, enum_type)) {
- enum_type->llvm_type = g->builtin_types.entry_void->llvm_type;
- enum_type->llvm_di_type = make_empty_namespace_llvm_di_type(g, import, buf_ptr(&enum_type->name),
- decl_node);
- enum_type->data.enumeration.resolve_status = ResolveStatusLLVMFull;
- return;
- }
-
- uint32_t field_count = enum_type->data.enumeration.src_field_count;
-
- assert(field_count == 0 || enum_type->data.enumeration.fields != nullptr);
- ZigLLVMDIEnumerator **di_enumerators = heap::c_allocator.allocate(field_count);
-
- for (uint32_t i = 0; i < field_count; i += 1) {
- TypeEnumField *enum_field = &enum_type->data.enumeration.fields[i];
-
- // TODO send patch to LLVM to support APInt in createEnumerator instead of int64_t
- // http://lists.llvm.org/pipermail/llvm-dev/2017-December/119456.html
- di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(enum_field->name),
- bigint_as_signed(&enum_field->value));
- }
-
- ZigType *tag_int_type = enum_type->data.enumeration.tag_int_type;
- enum_type->llvm_type = get_llvm_type(g, tag_int_type);
-
- // create debug type for tag
- uint64_t tag_debug_size_in_bits = 8*tag_int_type->abi_size;
- uint64_t tag_debug_align_in_bits = 8*tag_int_type->abi_align;
- ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,
- ZigLLVMFileToScope(import->data.structure.root_struct->di_file), buf_ptr(&enum_type->name),
- import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
- tag_debug_size_in_bits,
- tag_debug_align_in_bits,
- di_enumerators, field_count,
- get_llvm_di_type(g, tag_int_type), "");
-
- enum_type->llvm_di_type = tag_di_type;
- enum_type->data.enumeration.resolve_status = ResolveStatusLLVMFull;
-}
-
-static void resolve_llvm_types_union(CodeGen *g, ZigType *union_type, ResolveStatus wanted_resolve_status) {
- if (union_type->data.unionation.resolve_status >= wanted_resolve_status) return;
-
- bool packed = (union_type->data.unionation.layout == ContainerLayoutPacked);
- Scope *scope = &union_type->data.unionation.decls_scope->base;
- ZigType *import = get_scope_import(scope);
-
- TypeUnionField *most_aligned_union_member = union_type->data.unionation.most_aligned_union_member;
- ZigType *tag_type = union_type->data.unionation.tag_type;
- uint32_t gen_field_count = union_type->data.unionation.gen_field_count;
- if (gen_field_count == 0) {
- if (tag_type == nullptr) {
- union_type->llvm_type = g->builtin_types.entry_void->llvm_type;
- union_type->llvm_di_type = make_empty_namespace_llvm_di_type(g, import, buf_ptr(&union_type->name),
- union_type->data.unionation.decl_node);
- } else {
- union_type->llvm_type = get_llvm_type(g, tag_type);
- union_type->llvm_di_type = get_llvm_di_type(g, tag_type);
- }
- union_type->data.unionation.resolve_status = ResolveStatusLLVMFull;
- return;
- }
-
- AstNode *decl_node = union_type->data.unionation.decl_node;
-
- if (union_type->data.unionation.resolve_status < ResolveStatusLLVMFwdDecl) {
- union_type->llvm_type = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&union_type->name));
- size_t line = decl_node ? decl_node->line : 0;
- unsigned dwarf_kind = ZigLLVMTag_DW_structure_type();
- union_type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
- dwarf_kind, buf_ptr(&union_type->name),
- ZigLLVMFileToScope(import->data.structure.root_struct->di_file),
- import->data.structure.root_struct->di_file, (unsigned)(line + 1));
-
- union_type->data.unionation.resolve_status = ResolveStatusLLVMFwdDecl;
- if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) return;
- }
-
- ZigLLVMDIType **union_inner_di_types = heap::c_allocator.allocate(gen_field_count);
- uint32_t field_count = union_type->data.unionation.src_field_count;
- for (uint32_t i = 0; i < field_count; i += 1) {
- TypeUnionField *union_field = &union_type->data.unionation.fields[i];
- if (!type_has_bits(g, union_field->type_entry))
- continue;
-
- ZigLLVMDIType *field_di_type = get_llvm_di_type(g, union_field->type_entry);
- if (union_type->data.unionation.resolve_status >= wanted_resolve_status) return;
-
- uint64_t store_size_in_bits = union_field->type_entry->size_in_bits;
- uint64_t abi_align_in_bits = 8*union_field->type_entry->abi_align;
- AstNode *field_node = union_field->decl_node;
- union_inner_di_types[union_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(union_type->llvm_di_type), buf_ptr(union_field->enum_field->name),
- import->data.structure.root_struct->di_file, (unsigned)(field_node->line + 1),
- store_size_in_bits,
- abi_align_in_bits,
- 0,
- ZigLLVM_DIFlags_Zero, field_di_type);
-
- }
-
- if (tag_type == nullptr || !type_has_bits(g, tag_type)) {
- assert(most_aligned_union_member != nullptr);
-
- size_t padding_bytes = union_type->data.unionation.union_abi_size - most_aligned_union_member->type_entry->abi_size;
- if (padding_bytes > 0) {
- ZigType *u8_type = get_int_type(g, false, 8);
- ZigType *padding_array = get_array_type(g, u8_type, padding_bytes, nullptr);
- LLVMTypeRef union_element_types[] = {
- most_aligned_union_member->type_entry->llvm_type,
- get_llvm_type(g, padding_array),
- };
- LLVMStructSetBody(union_type->llvm_type, union_element_types, 2, packed);
- } else {
- LLVMStructSetBody(union_type->llvm_type, &most_aligned_union_member->type_entry->llvm_type, 1, packed);
- }
- union_type->data.unionation.union_llvm_type = union_type->llvm_type;
- union_type->data.unionation.gen_tag_index = SIZE_MAX;
- union_type->data.unionation.gen_union_index = SIZE_MAX;
-
- // create debug type for union
- ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder,
- ZigLLVMFileToScope(import->data.structure.root_struct->di_file), buf_ptr(&union_type->name),
- import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
- union_type->data.unionation.union_abi_size * 8,
- most_aligned_union_member->align * 8,
- ZigLLVM_DIFlags_Zero, union_inner_di_types,
- gen_field_count, 0, "");
-
- ZigLLVMReplaceTemporary(g->dbuilder, union_type->llvm_di_type, replacement_di_type);
- union_type->llvm_di_type = replacement_di_type;
- union_type->data.unionation.resolve_status = ResolveStatusLLVMFull;
- return;
- }
-
- LLVMTypeRef union_type_ref;
- size_t padding_bytes = union_type->data.unionation.union_abi_size - most_aligned_union_member->type_entry->abi_size;
- if (padding_bytes == 0) {
- union_type_ref = get_llvm_type(g, most_aligned_union_member->type_entry);
- } else {
- ZigType *u8_type = get_int_type(g, false, 8);
- ZigType *padding_array = get_array_type(g, u8_type, padding_bytes, nullptr);
- LLVMTypeRef union_element_types[] = {
- get_llvm_type(g, most_aligned_union_member->type_entry),
- get_llvm_type(g, padding_array),
- };
- union_type_ref = LLVMStructType(union_element_types, 2, false);
- }
- union_type->data.unionation.union_llvm_type = union_type_ref;
-
- LLVMTypeRef root_struct_element_types[2];
- root_struct_element_types[union_type->data.unionation.gen_tag_index] = get_llvm_type(g, tag_type);
- root_struct_element_types[union_type->data.unionation.gen_union_index] = union_type_ref;
- LLVMStructSetBody(union_type->llvm_type, root_struct_element_types, 2, packed);
-
- // create debug type for union
- ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder,
- ZigLLVMTypeToScope(union_type->llvm_di_type), "AnonUnion",
- import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
- most_aligned_union_member->type_entry->size_in_bits, 8*most_aligned_union_member->align,
- ZigLLVM_DIFlags_Zero, union_inner_di_types, gen_field_count, 0, "");
-
- uint64_t union_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, union_type->llvm_type,
- union_type->data.unionation.gen_union_index);
- uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, union_type->llvm_type,
- union_type->data.unionation.gen_tag_index);
-
- ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(union_type->llvm_di_type), "payload",
- import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
- most_aligned_union_member->type_entry->size_in_bits,
- 8*most_aligned_union_member->align,
- union_offset_in_bits,
- ZigLLVM_DIFlags_Zero, union_di_type);
-
- uint64_t tag_debug_size_in_bits = tag_type->size_in_bits;
- uint64_t tag_debug_align_in_bits = 8*tag_type->abi_align;
-
- ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(union_type->llvm_di_type), "tag",
- import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
- tag_debug_size_in_bits,
- tag_debug_align_in_bits,
- tag_offset_in_bits,
- ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, tag_type));
-
- ZigLLVMDIType *di_root_members[2];
- di_root_members[union_type->data.unionation.gen_tag_index] = tag_member_di_type;
- di_root_members[union_type->data.unionation.gen_union_index] = union_member_di_type;
-
- uint64_t debug_size_in_bits = union_type->size_in_bits;
- uint64_t debug_align_in_bits = 8*union_type->abi_align;
- ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
- ZigLLVMFileToScope(import->data.structure.root_struct->di_file),
- buf_ptr(&union_type->name),
- import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
- debug_size_in_bits,
- debug_align_in_bits,
- ZigLLVM_DIFlags_Zero, nullptr, di_root_members, 2, 0, nullptr, "");
-
- ZigLLVMReplaceTemporary(g->dbuilder, union_type->llvm_di_type, replacement_di_type);
- union_type->llvm_di_type = replacement_di_type;
- union_type->data.unionation.resolve_status = ResolveStatusLLVMFull;
-}
-
-static void resolve_llvm_types_pointer(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) {
- if (type->llvm_di_type != nullptr) return;
-
- if (resolve_pointer_zero_bits(g, type) != ErrorNone)
- zig_unreachable();
-
- if (!type_has_bits(g, type)) {
- type->llvm_type = g->builtin_types.entry_void->llvm_type;
- type->llvm_di_type = g->builtin_types.entry_void->llvm_di_type;
- return;
- }
-
- ZigType *elem_type = type->data.pointer.child_type;
-
- if (type->data.pointer.is_const || type->data.pointer.is_volatile ||
- type->data.pointer.explicit_alignment != 0 || type->data.pointer.ptr_len != PtrLenSingle ||
- type->data.pointer.bit_offset_in_host != 0 || type->data.pointer.allow_zero ||
- type->data.pointer.vector_index != VECTOR_INDEX_NONE || type->data.pointer.sentinel != nullptr)
- {
- assertNoError(type_resolve(g, elem_type, ResolveStatusLLVMFwdDecl));
- ZigType *peer_type;
- if (type->data.pointer.vector_index == VECTOR_INDEX_NONE) {
- peer_type = get_pointer_to_type_extra2(g, elem_type, false, false,
- PtrLenSingle, 0, 0, type->data.pointer.host_int_bytes, false,
- VECTOR_INDEX_NONE, nullptr, nullptr);
- } else {
- uint32_t host_vec_len = type->data.pointer.host_int_bytes;
- ZigType *host_vec_type = get_vector_type(g, host_vec_len, elem_type);
- peer_type = get_pointer_to_type_extra2(g, host_vec_type, false, false,
- PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, nullptr);
- }
- type->llvm_type = get_llvm_type(g, peer_type);
- type->llvm_di_type = get_llvm_di_type(g, peer_type);
- assertNoError(type_resolve(g, elem_type, wanted_resolve_status));
- return;
- }
-
- if (type->data.pointer.host_int_bytes == 0) {
- assertNoError(type_resolve(g, elem_type, ResolveStatusLLVMFwdDecl));
- type->llvm_type = LLVMPointerType(elem_type->llvm_type, 0);
- uint64_t debug_size_in_bits = 8*get_store_size_bytes(type->size_in_bits);
- uint64_t debug_align_in_bits = 8*type->abi_align;
- type->llvm_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, elem_type->llvm_di_type,
- debug_size_in_bits, debug_align_in_bits, buf_ptr(&type->name));
- assertNoError(type_resolve(g, elem_type, wanted_resolve_status));
- } else {
- ZigType *host_int_type = get_int_type(g, false, type->data.pointer.host_int_bytes * 8);
- LLVMTypeRef host_int_llvm_type = get_llvm_type(g, host_int_type);
- type->llvm_type = LLVMPointerType(host_int_llvm_type, 0);
- uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, host_int_llvm_type);
- uint64_t debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, host_int_llvm_type);
- type->llvm_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, get_llvm_di_type(g, host_int_type),
- debug_size_in_bits, debug_align_in_bits, buf_ptr(&type->name));
- }
-}
-
-static void resolve_llvm_types_integer(CodeGen *g, ZigType *type) {
- if (type->llvm_di_type != nullptr) return;
-
- if (!type_has_bits(g, type)) {
- type->llvm_type = g->builtin_types.entry_void->llvm_type;
- type->llvm_di_type = g->builtin_types.entry_void->llvm_di_type;
- return;
- }
-
- unsigned dwarf_tag;
- if (type->data.integral.is_signed) {
- if (type->size_in_bits == 8) {
- dwarf_tag = ZigLLVMEncoding_DW_ATE_signed_char();
- } else {
- dwarf_tag = ZigLLVMEncoding_DW_ATE_signed();
- }
- } else {
- if (type->size_in_bits == 8) {
- dwarf_tag = ZigLLVMEncoding_DW_ATE_unsigned_char();
- } else {
- dwarf_tag = ZigLLVMEncoding_DW_ATE_unsigned();
- }
- }
-
- type->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&type->name),
- type->abi_size * 8, dwarf_tag);
- type->llvm_type = LLVMIntType(type->size_in_bits);
-}
-
-static void resolve_llvm_types_optional(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) {
- assert(type->id == ZigTypeIdOptional);
- assert(type->data.maybe.resolve_status != ResolveStatusInvalid);
- assert(type->data.maybe.resolve_status >= ResolveStatusSizeKnown);
- if (type->data.maybe.resolve_status >= wanted_resolve_status) return;
-
- LLVMTypeRef bool_llvm_type = get_llvm_type(g, g->builtin_types.entry_bool);
- ZigLLVMDIType *bool_llvm_di_type = get_llvm_di_type(g, g->builtin_types.entry_bool);
-
- ZigType *child_type = type->data.maybe.child_type;
- if (!type_has_bits(g, child_type)) {
- type->llvm_type = bool_llvm_type;
- type->llvm_di_type = bool_llvm_di_type;
- type->data.maybe.resolve_status = ResolveStatusLLVMFull;
- return;
- }
-
- if (type_is_nonnull_ptr(g, child_type) || child_type->id == ZigTypeIdErrorSet) {
- type->llvm_type = get_llvm_type(g, child_type);
- type->llvm_di_type = get_llvm_di_type(g, child_type);
- type->data.maybe.resolve_status = ResolveStatusLLVMFull;
- return;
- }
-
- ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
- ZigLLVMDIFile *di_file = nullptr;
- unsigned line = 0;
-
- if (type->data.maybe.resolve_status < ResolveStatusLLVMFwdDecl) {
- type->llvm_type = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&type->name));
- unsigned dwarf_kind = ZigLLVMTag_DW_structure_type();
- type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
- dwarf_kind, buf_ptr(&type->name),
- compile_unit_scope, di_file, line);
-
- type->data.maybe.resolve_status = ResolveStatusLLVMFwdDecl;
- if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) return;
- }
-
- ZigLLVMDIType *child_llvm_di_type = get_llvm_di_type(g, child_type);
- if (type->data.maybe.resolve_status >= wanted_resolve_status) return;
-
- LLVMTypeRef elem_types[] = {
- get_llvm_type(g, child_type),
- LLVMInt1Type(),
- };
- LLVMStructSetBody(type->llvm_type, elem_types, 2, false);
-
- uint64_t val_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, maybe_child_index);
- uint64_t maybe_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, maybe_null_index);
-
- ZigLLVMDIType *di_element_types[2];
- di_element_types[maybe_child_index] =
- ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type),
- "val", di_file, line,
- 8 * child_type->abi_size,
- 8 * child_type->abi_align,
- val_offset_in_bits,
- ZigLLVM_DIFlags_Zero, child_llvm_di_type);
- di_element_types[maybe_null_index] =
- ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type),
- "maybe", di_file, line,
- 8*g->builtin_types.entry_bool->abi_size,
- 8*g->builtin_types.entry_bool->abi_align,
- maybe_offset_in_bits,
- ZigLLVM_DIFlags_Zero, bool_llvm_di_type);
- ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
- compile_unit_scope,
- buf_ptr(&type->name),
- di_file, line, 8 * type->abi_size, 8 * type->abi_align, ZigLLVM_DIFlags_Zero,
- nullptr, di_element_types, 2, 0, nullptr, "");
-
- ZigLLVMReplaceTemporary(g->dbuilder, type->llvm_di_type, replacement_di_type);
- type->llvm_di_type = replacement_di_type;
- type->data.maybe.resolve_status = ResolveStatusLLVMFull;
-}
-
-static void resolve_llvm_types_error_union(CodeGen *g, ZigType *type) {
- if (type->llvm_di_type != nullptr) return;
-
- ZigType *payload_type = type->data.error_union.payload_type;
- ZigType *err_set_type = type->data.error_union.err_set_type;
-
- if (!type_has_bits(g, payload_type)) {
- assert(type_has_bits(g, err_set_type));
- type->llvm_type = get_llvm_type(g, err_set_type);
- type->llvm_di_type = get_llvm_di_type(g, err_set_type);
- } else if (!type_has_bits(g, err_set_type)) {
- type->llvm_type = get_llvm_type(g, payload_type);
- type->llvm_di_type = get_llvm_di_type(g, payload_type);
- } else {
- LLVMTypeRef err_set_llvm_type = get_llvm_type(g, err_set_type);
- LLVMTypeRef payload_llvm_type = get_llvm_type(g, payload_type);
- LLVMTypeRef elem_types[3];
- elem_types[err_union_err_index] = err_set_llvm_type;
- elem_types[err_union_payload_index] = payload_llvm_type;
-
- type->llvm_type = LLVMStructType(elem_types, 2, false);
- if (LLVMABISizeOfType(g->target_data_ref, type->llvm_type) != type->abi_size) {
- // we need to do our own padding
- type->data.error_union.pad_llvm_type = LLVMArrayType(LLVMInt8Type(), type->data.error_union.pad_bytes);
- elem_types[2] = type->data.error_union.pad_llvm_type;
- type->llvm_type = LLVMStructType(elem_types, 3, false);
- }
-
- ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
- ZigLLVMDIFile *di_file = nullptr;
- unsigned line = 0;
- type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
- ZigLLVMTag_DW_structure_type(), buf_ptr(&type->name),
- compile_unit_scope, di_file, line);
-
- uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, err_set_llvm_type);
- uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, err_set_llvm_type);
- uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, err_union_err_index);
-
- uint64_t value_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, payload_llvm_type);
- uint64_t value_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, payload_llvm_type);
- uint64_t value_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type,
- err_union_payload_index);
-
- uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, type->llvm_type);
- uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, type->llvm_type);
-
- ZigLLVMDIType *di_element_types[2];
- di_element_types[err_union_err_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(type->llvm_di_type),
- "tag", di_file, line,
- tag_debug_size_in_bits,
- tag_debug_align_in_bits,
- tag_offset_in_bits,
- ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, err_set_type));
- di_element_types[err_union_payload_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(type->llvm_di_type),
- "value", di_file, line,
- value_debug_size_in_bits,
- value_debug_align_in_bits,
- value_offset_in_bits,
- ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, payload_type));
-
- ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
- compile_unit_scope,
- buf_ptr(&type->name),
- di_file, line,
- debug_size_in_bits,
- debug_align_in_bits,
- ZigLLVM_DIFlags_Zero,
- nullptr, di_element_types, 2, 0, nullptr, "");
-
- ZigLLVMReplaceTemporary(g->dbuilder, type->llvm_di_type, replacement_di_type);
- type->llvm_di_type = replacement_di_type;
- }
-}
-
-static void resolve_llvm_types_array(CodeGen *g, ZigType *type) {
- if (type->llvm_di_type != nullptr) return;
-
- if (!type_has_bits(g, type)) {
- type->llvm_type = g->builtin_types.entry_void->llvm_type;
- type->llvm_di_type = g->builtin_types.entry_void->llvm_di_type;
- return;
- }
-
- ZigType *elem_type = type->data.array.child_type;
-
- uint64_t extra_len_from_sentinel = (type->data.array.sentinel != nullptr) ? 1 : 0;
- uint64_t full_len = type->data.array.len + extra_len_from_sentinel;
- // TODO https://github.com/ziglang/zig/issues/1424
- type->llvm_type = LLVMArrayType(get_llvm_type(g, elem_type), (unsigned)full_len);
-
- uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, type->llvm_type);
- uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, type->llvm_type);
-
- type->llvm_di_type = ZigLLVMCreateDebugArrayType(g->dbuilder, debug_size_in_bits,
- debug_align_in_bits, get_llvm_di_type(g, elem_type), (int)full_len);
-}
-
-static void resolve_llvm_types_fn_type(CodeGen *g, ZigType *fn_type) {
- if (fn_type->llvm_di_type != nullptr) return;
-
- FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
- bool first_arg_return = want_first_arg_sret(g, fn_type_id);
- bool is_async = fn_type_id->cc == CallingConventionAsync;
- bool is_c_abi = !calling_convention_allows_zig_types(fn_type_id->cc);
- bool prefix_arg_error_return_trace = g->have_err_ret_tracing && fn_type_can_fail(fn_type_id);
- // +1 for maybe making the first argument the return value
- // +1 for maybe first argument the error return trace
- // +2 for maybe arguments async allocator and error code pointer
- ZigList gen_param_types = {};
- // +1 because 0 is the return type and
- // +1 for maybe making first arg ret val and
- // +1 for maybe first argument the error return trace
- // +2 for maybe arguments async allocator and error code pointer
- ZigList param_di_types = {};
- ZigType *gen_return_type;
- if (is_async) {
- gen_return_type = g->builtin_types.entry_void;
- param_di_types.append(nullptr);
- } else if (!type_has_bits(g, fn_type_id->return_type)) {
- gen_return_type = g->builtin_types.entry_void;
- param_di_types.append(nullptr);
- } else if (first_arg_return) {
- gen_return_type = g->builtin_types.entry_void;
- param_di_types.append(nullptr);
- ZigType *gen_type = get_pointer_to_type(g, fn_type_id->return_type, false);
- gen_param_types.append(get_llvm_type(g, gen_type));
- param_di_types.append(get_llvm_di_type(g, gen_type));
- } else {
- gen_return_type = fn_type_id->return_type;
- param_di_types.append(get_llvm_di_type(g, gen_return_type));
- }
- fn_type->data.fn.gen_return_type = gen_return_type;
-
- if (prefix_arg_error_return_trace && !is_async) {
- ZigType *gen_type = get_pointer_to_type(g, get_stack_trace_type(g), false);
- gen_param_types.append(get_llvm_type(g, gen_type));
- param_di_types.append(get_llvm_di_type(g, gen_type));
- }
- if (is_async) {
- fn_type->data.fn.gen_param_info = heap::c_allocator.allocate(2);
-
- ZigType *frame_type = get_any_frame_type(g, fn_type_id->return_type);
- gen_param_types.append(get_llvm_type(g, frame_type));
- param_di_types.append(get_llvm_di_type(g, frame_type));
-
- fn_type->data.fn.gen_param_info[0].src_index = 0;
- fn_type->data.fn.gen_param_info[0].gen_index = 0;
- fn_type->data.fn.gen_param_info[0].type = frame_type;
-
- gen_param_types.append(get_llvm_type(g, g->builtin_types.entry_usize));
- param_di_types.append(get_llvm_di_type(g, g->builtin_types.entry_usize));
-
- fn_type->data.fn.gen_param_info[1].src_index = 1;
- fn_type->data.fn.gen_param_info[1].gen_index = 1;
- fn_type->data.fn.gen_param_info[1].type = g->builtin_types.entry_usize;
- } else {
- fn_type->data.fn.gen_param_info = heap::c_allocator.allocate(fn_type_id->param_count);
- for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
- FnTypeParamInfo *src_param_info = &fn_type->data.fn.fn_type_id.param_info[i];
- ZigType *type_entry = src_param_info->type;
- FnGenParamInfo *gen_param_info = &fn_type->data.fn.gen_param_info[i];
-
- gen_param_info->src_index = i;
- gen_param_info->gen_index = SIZE_MAX;
-
- if (is_c_abi || !type_has_bits(g, type_entry))
- continue;
-
- ZigType *gen_type;
- if (handle_is_ptr(g, type_entry)) {
- gen_type = get_pointer_to_type(g, type_entry, true);
- gen_param_info->is_byval = true;
- } else {
- gen_type = type_entry;
- }
- gen_param_info->gen_index = gen_param_types.length;
- gen_param_info->type = gen_type;
- gen_param_types.append(get_llvm_type(g, gen_type));
-
- param_di_types.append(get_llvm_di_type(g, gen_type));
- }
- }
-
- if (is_c_abi) {
- FnWalk fn_walk = {};
- fn_walk.id = FnWalkIdTypes;
- fn_walk.data.types.param_di_types = ¶m_di_types;
- fn_walk.data.types.gen_param_types = &gen_param_types;
- walk_function_params(g, fn_type, &fn_walk);
- }
-
- fn_type->data.fn.gen_param_count = gen_param_types.length;
-
- for (size_t i = 0; i < gen_param_types.length; i += 1) {
- assert(gen_param_types.items[i] != nullptr);
- }
-
- fn_type->data.fn.raw_type_ref = LLVMFunctionType(get_llvm_type(g, gen_return_type),
- gen_param_types.items, (unsigned int)gen_param_types.length, fn_type_id->is_var_args);
- const unsigned fn_addrspace = ZigLLVMDataLayoutGetProgramAddressSpace(g->target_data_ref);
- fn_type->llvm_type = LLVMPointerType(fn_type->data.fn.raw_type_ref, fn_addrspace);
- fn_type->data.fn.raw_di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types.items, (int)param_di_types.length, 0);
- fn_type->llvm_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, fn_type->data.fn.raw_di_type,
- LLVMStoreSizeOfType(g->target_data_ref, fn_type->llvm_type),
- LLVMABIAlignmentOfType(g->target_data_ref, fn_type->llvm_type), "");
-
- gen_param_types.deinit();
- param_di_types.deinit();
-}
-
-void resolve_llvm_types_fn(CodeGen *g, ZigFn *fn) {
- Error err;
- if (fn->raw_di_type != nullptr) return;
-
- ZigType *fn_type = fn->type_entry;
- if (!fn_is_async(fn)) {
- resolve_llvm_types_fn_type(g, fn_type);
- fn->raw_type_ref = fn_type->data.fn.raw_type_ref;
- fn->raw_di_type = fn_type->data.fn.raw_di_type;
- return;
- }
-
- ZigType *gen_return_type = g->builtin_types.entry_void;
- ZigList param_di_types = {};
- ZigList gen_param_types = {};
- // first "parameter" is return value
- param_di_types.append(nullptr);
-
- ZigType *frame_type = get_fn_frame_type(g, fn);
- ZigType *ptr_type = get_pointer_to_type(g, frame_type, false);
- if ((err = type_resolve(g, ptr_type, ResolveStatusLLVMFwdDecl)))
- zig_unreachable();
- gen_param_types.append(ptr_type->llvm_type);
- param_di_types.append(ptr_type->llvm_di_type);
-
- // this parameter is used to pass the result pointer when await completes
- gen_param_types.append(get_llvm_type(g, g->builtin_types.entry_usize));
- param_di_types.append(get_llvm_di_type(g, g->builtin_types.entry_usize));
-
- fn->raw_type_ref = LLVMFunctionType(get_llvm_type(g, gen_return_type),
- gen_param_types.items, gen_param_types.length, false);
- fn->raw_di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types.items, (int)param_di_types.length, 0);
-
- param_di_types.deinit();
- gen_param_types.deinit();
-}
-
-static void resolve_llvm_types_anyerror(CodeGen *g) {
- ZigType *entry = g->builtin_types.entry_global_error_set;
- entry->llvm_type = get_llvm_type(g, g->err_tag_type);
- ZigList err_enumerators = {};
- // reserve index 0 to indicate no error
- err_enumerators.append(ZigLLVMCreateDebugEnumerator(g->dbuilder, "(none)", 0));
- for (size_t i = 1; i < g->errors_by_index.length; i += 1) {
- ErrorTableEntry *error_entry = g->errors_by_index.at(i);
- err_enumerators.append(ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(&error_entry->name), i));
- }
-
- // create debug type for error sets
- uint64_t tag_debug_size_in_bits = g->err_tag_type->size_in_bits;
- uint64_t tag_debug_align_in_bits = 8*g->err_tag_type->abi_align;
- ZigLLVMDIFile *err_set_di_file = nullptr;
- entry->llvm_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,
- ZigLLVMCompileUnitToScope(g->compile_unit), buf_ptr(&entry->name),
- err_set_di_file, 0,
- tag_debug_size_in_bits,
- tag_debug_align_in_bits,
- err_enumerators.items, err_enumerators.length,
- get_llvm_di_type(g, g->err_tag_type), "");
-
- err_enumerators.deinit();
-}
-
-static void resolve_llvm_types_async_frame(CodeGen *g, ZigType *frame_type, ResolveStatus wanted_resolve_status) {
- Error err;
- if ((err = type_resolve(g, frame_type, ResolveStatusSizeKnown)))
- zig_unreachable();
-
- ZigType *passed_frame_type = fn_is_async(frame_type->data.frame.fn) ? frame_type : nullptr;
- resolve_llvm_types_struct(g, frame_type->data.frame.locals_struct, wanted_resolve_status, passed_frame_type);
- frame_type->llvm_type = frame_type->data.frame.locals_struct->llvm_type;
- frame_type->llvm_di_type = frame_type->data.frame.locals_struct->llvm_di_type;
-}
-
-static void resolve_llvm_types_any_frame(CodeGen *g, ZigType *any_frame_type, ResolveStatus wanted_resolve_status) {
- if (any_frame_type->llvm_di_type != nullptr) return;
-
- Buf *name = buf_sprintf("(%s header)", buf_ptr(&any_frame_type->name));
- LLVMTypeRef frame_header_type = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(name));
- any_frame_type->llvm_type = LLVMPointerType(frame_header_type, 0);
-
- unsigned dwarf_kind = ZigLLVMTag_DW_structure_type();
- ZigLLVMDIFile *di_file = nullptr;
- ZigLLVMDIScope *di_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
- unsigned line = 0;
- ZigLLVMDIType *frame_header_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
- dwarf_kind, buf_ptr(name), di_scope, di_file, line);
- any_frame_type->llvm_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, frame_header_di_type,
- 8*g->pointer_size_bytes, 8*g->builtin_types.entry_usize->abi_align, buf_ptr(&any_frame_type->name));
-
- LLVMTypeRef llvm_void = LLVMVoidType();
- LLVMTypeRef arg_types[] = {any_frame_type->llvm_type, g->builtin_types.entry_usize->llvm_type};
- LLVMTypeRef fn_type = LLVMFunctionType(llvm_void, arg_types, 2, false);
- LLVMTypeRef usize_type_ref = get_llvm_type(g, g->builtin_types.entry_usize);
- ZigLLVMDIType *usize_di_type = get_llvm_di_type(g, g->builtin_types.entry_usize);
- ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
-
- ZigType *result_type = any_frame_type->data.any_frame.result_type;
- ZigType *ptr_result_type = (result_type == nullptr) ? nullptr : get_pointer_to_type(g, result_type, false);
- const unsigned fn_addrspace = ZigLLVMDataLayoutGetProgramAddressSpace(g->target_data_ref);
- LLVMTypeRef ptr_fn_llvm_type = LLVMPointerType(fn_type, fn_addrspace);
- if (result_type == nullptr) {
- g->anyframe_fn_type = ptr_fn_llvm_type;
- }
-
- ZigList field_types = {};
- ZigList di_element_types = {};
-
- // label (grep this): [fn_frame_struct_layout]
- field_types.append(ptr_fn_llvm_type); // fn_ptr
- field_types.append(usize_type_ref); // resume_index
- field_types.append(usize_type_ref); // awaiter
-
- bool have_result_type = result_type != nullptr && type_has_bits(g, result_type);
- if (have_result_type) {
- field_types.append(get_llvm_type(g, ptr_result_type)); // result_ptr_callee
- field_types.append(get_llvm_type(g, ptr_result_type)); // result_ptr_awaiter
- field_types.append(get_llvm_type(g, result_type)); // result
- if (codegen_fn_has_err_ret_tracing_arg(g, result_type)) {
- ZigType *ptr_stack_trace = get_pointer_to_type(g, get_stack_trace_type(g), false);
- field_types.append(get_llvm_type(g, ptr_stack_trace)); // ptr_stack_trace_callee
- field_types.append(get_llvm_type(g, ptr_stack_trace)); // ptr_stack_trace_awaiter
- }
- }
- LLVMStructSetBody(frame_header_type, field_types.items, field_types.length, false);
-
- di_element_types.append(
- ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "fn_ptr",
- di_file, line,
- 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
- ZigLLVM_DIFlags_Zero, usize_di_type));
- di_element_types.append(
- ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "resume_index",
- di_file, line,
- 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
- ZigLLVM_DIFlags_Zero, usize_di_type));
- di_element_types.append(
- ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "awaiter",
- di_file, line,
- 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
- ZigLLVM_DIFlags_Zero, usize_di_type));
-
- if (have_result_type) {
- di_element_types.append(
- ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "result_ptr_callee",
- di_file, line,
- 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
- ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, ptr_result_type)));
- di_element_types.append(
- ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "result_ptr_awaiter",
- di_file, line,
- 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
- ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, ptr_result_type)));
- di_element_types.append(
- ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "result",
- di_file, line,
- 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
- ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, result_type)));
-
- if (codegen_fn_has_err_ret_tracing_arg(g, result_type)) {
- ZigType *ptr_stack_trace = get_pointer_to_type(g, get_stack_trace_type(g), false);
- di_element_types.append(
- ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "ptr_stack_trace_callee",
- di_file, line,
- 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
- ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, ptr_stack_trace)));
- di_element_types.append(
- ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "ptr_stack_trace_awaiter",
- di_file, line,
- 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
- ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, ptr_stack_trace)));
- }
- };
-
- ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
- compile_unit_scope, buf_ptr(name),
- di_file, line,
- 8*LLVMABISizeOfType(g->target_data_ref, frame_header_type),
- 8*LLVMABIAlignmentOfType(g->target_data_ref, frame_header_type),
- ZigLLVM_DIFlags_Zero,
- nullptr, di_element_types.items, di_element_types.length, 0, nullptr, "");
-
- ZigLLVMReplaceTemporary(g->dbuilder, frame_header_di_type, replacement_di_type);
-
- field_types.deinit();
- di_element_types.deinit();
-}
-
-static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) {
- assert(wanted_resolve_status > ResolveStatusSizeKnown);
- switch (type->id) {
- case ZigTypeIdInvalid:
- case ZigTypeIdMetaType:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdBoundFn:
- zig_unreachable();
- case ZigTypeIdFloat:
- case ZigTypeIdOpaque:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdUnreachable:
- assert(type->llvm_di_type != nullptr);
- return;
- case ZigTypeIdStruct:
- if (type->data.structure.special == StructSpecialSlice)
- return resolve_llvm_types_slice(g, type, wanted_resolve_status);
- else
- return resolve_llvm_types_struct(g, type, wanted_resolve_status, nullptr);
- case ZigTypeIdEnum:
- return resolve_llvm_types_enum(g, type, wanted_resolve_status);
- case ZigTypeIdUnion:
- return resolve_llvm_types_union(g, type, wanted_resolve_status);
- case ZigTypeIdPointer:
- return resolve_llvm_types_pointer(g, type, wanted_resolve_status);
- case ZigTypeIdInt:
- return resolve_llvm_types_integer(g, type);
- case ZigTypeIdOptional:
- return resolve_llvm_types_optional(g, type, wanted_resolve_status);
- case ZigTypeIdErrorUnion:
- return resolve_llvm_types_error_union(g, type);
- case ZigTypeIdArray:
- return resolve_llvm_types_array(g, type);
- case ZigTypeIdFn:
- return resolve_llvm_types_fn_type(g, type);
- case ZigTypeIdErrorSet: {
- if (type->llvm_di_type != nullptr) return;
-
- if (g->builtin_types.entry_global_error_set->llvm_type == nullptr) {
- resolve_llvm_types_anyerror(g);
- }
- type->llvm_type = g->builtin_types.entry_global_error_set->llvm_type;
- type->llvm_di_type = g->builtin_types.entry_global_error_set->llvm_di_type;
- return;
- }
- case ZigTypeIdVector: {
- if (type->llvm_di_type != nullptr) return;
-
- type->llvm_type = LLVMVectorType(get_llvm_type(g, type->data.vector.elem_type), type->data.vector.len);
- type->llvm_di_type = ZigLLVMDIBuilderCreateVectorType(g->dbuilder, 8 * type->abi_size,
- type->abi_align, get_llvm_di_type(g, type->data.vector.elem_type), type->data.vector.len);
- return;
- }
- case ZigTypeIdFnFrame:
- return resolve_llvm_types_async_frame(g, type, wanted_resolve_status);
- case ZigTypeIdAnyFrame:
- return resolve_llvm_types_any_frame(g, type, wanted_resolve_status);
- }
- zig_unreachable();
-}
-
-LLVMTypeRef get_llvm_type(CodeGen *g, ZigType *type) {
- assertNoError(type_resolve(g, type, ResolveStatusLLVMFull));
- assert(type->abi_size == 0 || type->abi_size >= LLVMABISizeOfType(g->target_data_ref, type->llvm_type));
- assert(type->abi_align == 0 || type->abi_align >= LLVMABIAlignmentOfType(g->target_data_ref, type->llvm_type));
- return type->llvm_type;
-}
-
-ZigLLVMDIType *get_llvm_di_type(CodeGen *g, ZigType *type) {
- assertNoError(type_resolve(g, type, ResolveStatusLLVMFull));
- return type->llvm_di_type;
-}
-
-void src_assert_impl(bool ok, AstNode *source_node, char const *file, unsigned int line) {
- if (ok) return;
- if (source_node == nullptr) {
- fprintf(stderr, "when analyzing (unknown source location) ");
- } else {
- fprintf(stderr, "when analyzing %s:%u:%u ",
- buf_ptr(source_node->owner->data.structure.root_struct->path),
- (unsigned)source_node->line + 1, (unsigned)source_node->column + 1);
- }
- fprintf(stderr, "in compiler source at %s:%u: ", file, line);
- const char *msg = "assertion failed. This is a bug in the Zig compiler.";
- stage2_panic(msg, strlen(msg));
-}
-
-Error analyze_import(CodeGen *g, ZigType *source_import, Buf *import_target_str,
- ZigType **out_import, Buf **out_import_target_path, Buf *out_full_path)
-{
- Error err;
-
- Buf *search_dir;
- ZigPackage *cur_scope_pkg = source_import->data.structure.root_struct->package;
- assert(cur_scope_pkg);
- ZigPackage *target_package;
- auto package_entry = cur_scope_pkg->package_table.maybe_get(import_target_str);
- SourceKind source_kind;
- if (package_entry) {
- target_package = package_entry->value;
- *out_import_target_path = &target_package->root_src_path;
- search_dir = &target_package->root_src_dir;
- source_kind = SourceKindPkgMain;
- } else {
- // try it as a filename
- target_package = cur_scope_pkg;
- *out_import_target_path = import_target_str;
-
- // search relative to importing file
- search_dir = buf_alloc();
- os_path_dirname(source_import->data.structure.root_struct->path, search_dir);
-
- source_kind = SourceKindNonRoot;
- }
-
- buf_resize(out_full_path, 0);
- os_path_join(search_dir, *out_import_target_path, out_full_path);
-
- Buf *import_code = buf_alloc();
- Buf *resolved_path = buf_alloc();
-
- Buf *resolve_paths[] = { out_full_path, };
- *resolved_path = os_path_resolve(resolve_paths, 1);
-
- auto import_entry = g->import_table.maybe_get(resolved_path);
- if (import_entry) {
- *out_import = import_entry->value;
- return ErrorNone;
- }
-
- if (source_kind == SourceKindNonRoot) {
- Buf *pkg_root_src_dir = &cur_scope_pkg->root_src_dir;
- Buf resolved_root_src_dir = os_path_resolve(&pkg_root_src_dir, 1);
- if (!buf_starts_with_buf(resolved_path, &resolved_root_src_dir)) {
- return ErrorImportOutsidePkgPath;
- }
- }
-
- if ((err = file_fetch(g, resolved_path, import_code))) {
- return err;
- }
-
- *out_import = add_source_file(g, target_package, resolved_path, import_code, source_kind);
- return ErrorNone;
-}
-
-
-void IrExecutableSrc::src() {
- if (this->source_node != nullptr) {
- this->source_node->src();
- }
- if (this->parent_exec != nullptr) {
- this->parent_exec->src();
- }
-}
-
-void IrExecutableGen::src() {
- IrExecutableGen *it;
- for (it = this; it != nullptr && it->source_node != nullptr; it = it->parent_exec) {
- it->source_node->src();
- }
-}
-
-bool is_anon_container(ZigType *ty) {
- return ty->id == ZigTypeIdStruct && (
- ty->data.structure.special == StructSpecialInferredTuple ||
- ty->data.structure.special == StructSpecialInferredStruct);
-}
-
-bool is_opt_err_set(ZigType *ty) {
- return ty->id == ZigTypeIdErrorSet ||
- (ty->id == ZigTypeIdOptional && ty->data.maybe.child_type->id == ZigTypeIdErrorSet);
-}
-
-// Returns whether the x_optional field of ZigValue is active.
-bool type_has_optional_repr(ZigType *ty) {
- if (ty->id != ZigTypeIdOptional) {
- return false;
- } else if (get_src_ptr_type(ty) != nullptr) {
- return false;
- } else if (is_opt_err_set(ty)) {
- return false;
- } else {
- return true;
- }
-}
-
-void copy_const_val(CodeGen *g, ZigValue *dest, ZigValue *src) {
- uint32_t prev_align = dest->llvm_align;
- ConstParent prev_parent = dest->parent;
- memcpy(dest, src, sizeof(ZigValue));
- dest->llvm_align = prev_align;
- if (src->special != ConstValSpecialStatic)
- return;
- dest->parent = prev_parent;
- if (dest->type->id == ZigTypeIdStruct) {
- dest->data.x_struct.fields = alloc_const_vals_ptrs(g, dest->type->data.structure.src_field_count);
- for (size_t i = 0; i < dest->type->data.structure.src_field_count; i += 1) {
- copy_const_val(g, dest->data.x_struct.fields[i], src->data.x_struct.fields[i]);
- dest->data.x_struct.fields[i]->parent.id = ConstParentIdStruct;
- dest->data.x_struct.fields[i]->parent.data.p_struct.struct_val = dest;
- dest->data.x_struct.fields[i]->parent.data.p_struct.field_index = i;
- }
- } else if (dest->type->id == ZigTypeIdArray) {
- switch (dest->data.x_array.special) {
- case ConstArraySpecialNone: {
- dest->data.x_array.data.s_none.elements = g->pass1_arena->allocate(dest->type->data.array.len);
- for (uint64_t i = 0; i < dest->type->data.array.len; i += 1) {
- copy_const_val(g, &dest->data.x_array.data.s_none.elements[i], &src->data.x_array.data.s_none.elements[i]);
- dest->data.x_array.data.s_none.elements[i].parent.id = ConstParentIdArray;
- dest->data.x_array.data.s_none.elements[i].parent.data.p_array.array_val = dest;
- dest->data.x_array.data.s_none.elements[i].parent.data.p_array.elem_index = i;
- }
- break;
- }
- case ConstArraySpecialUndef: {
- // Nothing to copy; the above memcpy did everything we needed.
- break;
- }
- case ConstArraySpecialBuf: {
- dest->data.x_array.data.s_buf = buf_create_from_buf(src->data.x_array.data.s_buf);
- break;
- }
- }
- } else if (dest->type->id == ZigTypeIdUnion) {
- bigint_init_bigint(&dest->data.x_union.tag, &src->data.x_union.tag);
- dest->data.x_union.payload = g->pass1_arena->create();
- copy_const_val(g, dest->data.x_union.payload, src->data.x_union.payload);
- dest->data.x_union.payload->parent.id = ConstParentIdUnion;
- dest->data.x_union.payload->parent.data.p_union.union_val = dest;
- } else if (type_has_optional_repr(dest->type) && dest->data.x_optional != nullptr) {
- dest->data.x_optional = g->pass1_arena->create();
- copy_const_val(g, dest->data.x_optional, src->data.x_optional);
- dest->data.x_optional->parent.id = ConstParentIdOptionalPayload;
- dest->data.x_optional->parent.data.p_optional_payload.optional_val = dest;
- }
-}
-
-bool optional_value_is_null(ZigValue *val) {
- assert(val->special == ConstValSpecialStatic);
- if (get_src_ptr_type(val->type) != nullptr) {
- if (val->data.x_ptr.special == ConstPtrSpecialNull) {
- return true;
- } else if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) {
- return val->data.x_ptr.data.hard_coded_addr.addr == 0;
- } else {
- return false;
- }
- } else if (is_opt_err_set(val->type)) {
- return val->data.x_err_set == nullptr;
- } else {
- return val->data.x_optional == nullptr;
- }
-}
-
-bool type_is_numeric(ZigType *ty) {
- switch (ty->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdInt:
- case ZigTypeIdFloat:
- case ZigTypeIdUndefined:
- return true;
-
- case ZigTypeIdVector:
- return type_is_numeric(ty->data.vector.elem_type);
-
- case ZigTypeIdMetaType:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdUnreachable:
- case ZigTypeIdPointer:
- case ZigTypeIdArray:
- case ZigTypeIdStruct:
- case ZigTypeIdNull:
- case ZigTypeIdOptional:
- case ZigTypeIdErrorUnion:
- case ZigTypeIdErrorSet:
- case ZigTypeIdEnum:
- case ZigTypeIdUnion:
- case ZigTypeIdFn:
- case ZigTypeIdBoundFn:
- case ZigTypeIdOpaque:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- case ZigTypeIdEnumLiteral:
- return false;
- }
- zig_unreachable();
-}
-
-static void dump_value_indent_error_set(ZigValue *val, int indent) {
- fprintf(stderr, "\n");
-}
-
-static void dump_value_indent(ZigValue *val, int indent);
-
-static void dump_value_indent_ptr(ZigValue *val, int indent) {
- switch (val->data.x_ptr.special) {
- case ConstPtrSpecialInvalid:
- fprintf(stderr, "\n");
- return;
- case ConstPtrSpecialNull:
- fprintf(stderr, "\n");
- return;
- case ConstPtrSpecialRef:
- fprintf(stderr, "data.x_ptr.data.ref.pointee, indent + 1);
- break;
- case ConstPtrSpecialBaseStruct: {
- ZigValue *struct_val = val->data.x_ptr.data.base_struct.struct_val;
- size_t field_index = val->data.x_ptr.data.base_struct.field_index;
- fprintf(stderr, "data.x_struct.fields[field_index];
- if (field_val != nullptr) {
- dump_value_indent(field_val, indent + 1);
- } else {
- for (int i = 0; i < indent; i += 1) {
- fprintf(stderr, " ");
- }
- fprintf(stderr, "(invalid null field)\n");
- }
- }
- break;
- }
- case ConstPtrSpecialBaseOptionalPayload: {
- ZigValue *optional_val = val->data.x_ptr.data.base_optional_payload.optional_val;
- fprintf(stderr, "\n");
-}
-
-static void dump_value_indent(ZigValue *val, int indent) {
- for (int i = 0; i < indent; i += 1) {
- fprintf(stderr, " ");
- }
- fprintf(stderr, "Value@%p(", val);
- if (val->type != nullptr) {
- fprintf(stderr, "%s)", buf_ptr(&val->type->name));
- } else {
- fprintf(stderr, "type=nullptr)");
- }
- switch (val->special) {
- case ConstValSpecialUndef:
- fprintf(stderr, "[undefined]\n");
- return;
- case ConstValSpecialLazy:
- fprintf(stderr, "[lazy]\n");
- return;
- case ConstValSpecialRuntime:
- fprintf(stderr, "[runtime]\n");
- return;
- case ConstValSpecialStatic:
- break;
- }
- if (val->type == nullptr)
- return;
- switch (val->type->id) {
- case ZigTypeIdInvalid:
- fprintf(stderr, "\n");
- return;
- case ZigTypeIdUnreachable:
- fprintf(stderr, "\n");
- return;
- case ZigTypeIdUndefined:
- fprintf(stderr, "\n");
- return;
- case ZigTypeIdVoid:
- fprintf(stderr, "<{}>\n");
- return;
- case ZigTypeIdMetaType:
- fprintf(stderr, "<%s>\n", buf_ptr(&val->data.x_type->name));
- return;
- case ZigTypeIdBool:
- fprintf(stderr, "<%s>\n", val->data.x_bool ? "true" : "false");
- return;
- case ZigTypeIdComptimeInt:
- case ZigTypeIdInt: {
- Buf *tmp_buf = buf_alloc();
- bigint_append_buf(tmp_buf, &val->data.x_bigint, 10);
- fprintf(stderr, "<%s>\n", buf_ptr(tmp_buf));
- buf_destroy(tmp_buf);
- return;
- }
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdFloat:
- fprintf(stderr, "\n");
- return;
-
- case ZigTypeIdStruct:
- fprintf(stderr, "type->data.structure.src_field_count; i += 1) {
- for (int j = 0; j < indent; j += 1) {
- fprintf(stderr, " ");
- }
- fprintf(stderr, "%s: ", buf_ptr(val->type->data.structure.fields[i]->name));
- if (val->data.x_struct.fields == nullptr) {
- fprintf(stderr, "\n");
- } else {
- dump_value_indent(val->data.x_struct.fields[i], 1);
- }
- }
- for (int i = 0; i < indent; i += 1) {
- fprintf(stderr, " ");
- }
- fprintf(stderr, ">\n");
- return;
-
- case ZigTypeIdOptional:
- if (get_src_ptr_type(val->type) != nullptr) {
- return dump_value_indent_ptr(val, indent);
- } else if (val->type->data.maybe.child_type->id == ZigTypeIdErrorSet) {
- return dump_value_indent_error_set(val, indent);
- } else {
- fprintf(stderr, "<\n");
- dump_value_indent(val->data.x_optional, indent + 1);
-
- for (int i = 0; i < indent; i += 1) {
- fprintf(stderr, " ");
- }
- fprintf(stderr, ">\n");
- return;
- }
- case ZigTypeIdErrorUnion:
- if (val->data.x_err_union.payload != nullptr) {
- fprintf(stderr, "<\n");
- dump_value_indent(val->data.x_err_union.payload, indent + 1);
- } else {
- fprintf(stderr, "<\n");
- dump_value_indent(val->data.x_err_union.error_set, 0);
- }
- for (int i = 0; i < indent; i += 1) {
- fprintf(stderr, " ");
- }
- fprintf(stderr, ">\n");
- return;
-
- case ZigTypeIdPointer:
- return dump_value_indent_ptr(val, indent);
-
- case ZigTypeIdErrorSet:
- return dump_value_indent_error_set(val, indent);
-
- case ZigTypeIdVector:
- case ZigTypeIdArray:
- case ZigTypeIdNull:
- case ZigTypeIdEnum:
- case ZigTypeIdUnion:
- case ZigTypeIdFn:
- case ZigTypeIdBoundFn:
- case ZigTypeIdOpaque:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- case ZigTypeIdEnumLiteral:
- fprintf(stderr, "\n");
- return;
- }
- zig_unreachable();
-}
-
-void ZigValue::dump() {
- dump_value_indent(this, 0);
-}
-
-// float ops that take a single argument
-//TODO Powi, Pow, minnum, maxnum, maximum, minimum, copysign, lround, llround, lrint, llrint
-const char *float_op_to_name(BuiltinFnId op) {
- switch (op) {
- case BuiltinFnIdSqrt:
- return "sqrt";
- case BuiltinFnIdSin:
- return "sin";
- case BuiltinFnIdCos:
- return "cos";
- case BuiltinFnIdExp:
- return "exp";
- case BuiltinFnIdExp2:
- return "exp2";
- case BuiltinFnIdLog:
- return "log";
- case BuiltinFnIdLog10:
- return "log10";
- case BuiltinFnIdLog2:
- return "log2";
- case BuiltinFnIdFabs:
- return "fabs";
- case BuiltinFnIdFloor:
- return "floor";
- case BuiltinFnIdCeil:
- return "ceil";
- case BuiltinFnIdTrunc:
- return "trunc";
- case BuiltinFnIdNearbyInt:
- return "nearbyint";
- case BuiltinFnIdRound:
- return "round";
- default:
- zig_unreachable();
- }
-}
-
diff --git a/src/analyze.hpp b/src/analyze.hpp
deleted file mode 100644
index 0df1a4ba91e1eb37d057ffdf24beab123a08bba8..0000000000000000000000000000000000000000
--- a/src/analyze.hpp
+++ /dev/null
@@ -1,300 +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_ANALYZE_HPP
-#define ZIG_ANALYZE_HPP
-
-#include "all_types.hpp"
-
-void semantic_analyze(CodeGen *g);
-ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg);
-ErrorMsg *add_token_error(CodeGen *g, ZigType *owner, Token *token, Buf *msg);
-ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, const AstNode *node, Buf *msg);
-ZigType *new_type_table_entry(ZigTypeId id);
-ZigType *get_fn_frame_type(CodeGen *g, ZigFn *fn);
-ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const);
-ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type,
- bool is_const, bool is_volatile, PtrLen ptr_len,
- uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count,
- bool allow_zero);
-ZigType *get_pointer_to_type_extra2(CodeGen *g, ZigType *child_type,
- bool is_const, bool is_volatile, PtrLen ptr_len,
- uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count,
- bool allow_zero, uint32_t vector_index, InferredStructField *inferred_struct_field,
- ZigValue *sentinel);
-uint64_t type_size(CodeGen *g, ZigType *type_entry);
-uint64_t type_size_bits(CodeGen *g, ZigType *type_entry);
-ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits);
-ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type);
-ZigType **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type);
-ZigType *get_c_int_type(CodeGen *g, CIntType c_int_type);
-ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id);
-ZigType *get_optional_type(CodeGen *g, ZigType *child_type);
-ZigType *get_optional_type2(CodeGen *g, ZigType *child_type);
-ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, ZigValue *sentinel);
-ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type);
-ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind,
- AstNode *decl_node, const char *full_name, Buf *bare_name, ContainerLayout layout);
-ZigType *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x);
-ZigType *get_error_union_type(CodeGen *g, ZigType *err_set_type, ZigType *payload_type);
-ZigType *get_bound_fn_type(CodeGen *g, ZigFn *fn_entry);
-ZigType *get_opaque_type(CodeGen *g, Scope *scope, AstNode *source_node, const char *full_name, Buf *bare_name);
-ZigType *get_test_fn_type(CodeGen *g);
-ZigType *get_any_frame_type(CodeGen *g, ZigType *result_type);
-bool handle_is_ptr(CodeGen *g, ZigType *type_entry);
-
-bool type_has_bits(CodeGen *g, ZigType *type_entry);
-Error type_has_bits2(CodeGen *g, ZigType *type_entry, bool *result);
-
-Error type_allowed_in_extern(CodeGen *g, ZigType *type_entry, bool *result);
-bool ptr_allows_addr_zero(ZigType *ptr_type);
-
-// Deprecated, use `type_is_nonnull_ptr2`
-bool type_is_nonnull_ptr(CodeGen *g, ZigType *type);
-Error type_is_nonnull_ptr2(CodeGen *g, ZigType *type, bool *result);
-
-ZigType *get_codegen_ptr_type_bail(CodeGen *g, ZigType *type);
-Error get_codegen_ptr_type(CodeGen *g, ZigType *type, ZigType **result);
-
-enum SourceKind {
- SourceKindRoot,
- SourceKindPkgMain,
- SourceKindNonRoot,
- SourceKindCImport,
-};
-ZigType *add_source_file(CodeGen *g, ZigPackage *package, Buf *abs_full_path, Buf *source_code,
- SourceKind source_kind);
-
-ZigVar *find_variable(CodeGen *g, Scope *orig_context, Buf *name, ScopeFnDef **crossed_fndef_scope);
-Tld *find_decl(CodeGen *g, Scope *scope, Buf *name);
-Tld *find_container_decl(CodeGen *g, ScopeDecls *decls_scope, Buf *name);
-void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node, bool allow_lazy);
-
-ZigType *get_src_ptr_type(ZigType *type);
-uint32_t get_ptr_align(CodeGen *g, ZigType *type);
-bool get_ptr_const(CodeGen *g, ZigType *type);
-ZigType *validate_var_type(CodeGen *g, AstNodeVariableDeclaration *source_node, ZigType *type_entry);
-ZigType *container_ref_type(ZigType *type_entry);
-bool type_is_complete(ZigType *type_entry);
-bool type_is_resolved(ZigType *type_entry, ResolveStatus status);
-bool type_is_invalid(ZigType *type_entry);
-bool type_is_global_error_set(ZigType *err_set_type);
-ScopeDecls *get_container_scope(ZigType *type_entry);
-TypeStructField *find_struct_type_field(ZigType *type_entry, Buf *name);
-TypeEnumField *find_enum_type_field(ZigType *enum_type, Buf *name);
-TypeUnionField *find_union_type_field(ZigType *type_entry, Buf *name);
-TypeEnumField *find_enum_field_by_tag(ZigType *enum_type, const BigInt *tag);
-TypeUnionField *find_union_field_by_tag(ZigType *type_entry, const BigInt *tag);
-
-bool is_ref(ZigType *type_entry);
-bool is_array_ref(ZigType *type_entry);
-bool is_container_ref(ZigType *type_entry);
-Error is_valid_vector_elem_type(CodeGen *g, ZigType *elem_type, bool *result);
-void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node);
-ZigFn *scope_fn_entry(Scope *scope);
-ZigPackage *scope_package(Scope *scope);
-ZigType *get_scope_import(Scope *scope);
-ScopeTypeOf *get_scope_typeof(Scope *scope);
-void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node, Scope *parent_scope);
-ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name,
- bool is_const, ZigValue *init_value, Tld *src_tld, ZigType *var_type);
-ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node);
-void append_namespace_qualification(CodeGen *g, Buf *buf, ZigType *container_type);
-ZigFn *create_fn(CodeGen *g, AstNode *proto_node);
-void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, CallingConvention cc, size_t param_count_alloc);
-AstNode *get_param_decl_node(ZigFn *fn_entry, size_t index);
-Error ATTRIBUTE_MUST_USE type_resolve(CodeGen *g, ZigType *type_entry, ResolveStatus status);
-void complete_enum(CodeGen *g, ZigType *enum_type);
-bool ir_get_var_is_comptime(ZigVar *var);
-bool const_values_equal(CodeGen *g, ZigValue *a, ZigValue *b);
-void eval_min_max_value(CodeGen *g, ZigType *type_entry, ZigValue *const_val, bool is_max);
-void eval_min_max_value_int(CodeGen *g, ZigType *int_type, BigInt *bigint, bool is_max);
-
-void render_const_value(CodeGen *g, Buf *buf, ZigValue *const_val);
-
-ScopeDecls *create_decls_scope(CodeGen *g, AstNode *node, Scope *parent, ZigType *container_type, ZigType *import, Buf *bare_name);
-ScopeBlock *create_block_scope(CodeGen *g, AstNode *node, Scope *parent);
-ScopeDefer *create_defer_scope(CodeGen *g, AstNode *node, Scope *parent);
-ScopeDeferExpr *create_defer_expr_scope(CodeGen *g, AstNode *node, Scope *parent);
-Scope *create_var_scope(CodeGen *g, AstNode *node, Scope *parent, ZigVar *var);
-ScopeCImport *create_cimport_scope(CodeGen *g, AstNode *node, Scope *parent);
-ScopeLoop *create_loop_scope(CodeGen *g, AstNode *node, Scope *parent);
-ScopeSuspend *create_suspend_scope(CodeGen *g, AstNode *node, Scope *parent);
-ScopeFnDef *create_fndef_scope(CodeGen *g, AstNode *node, Scope *parent, ZigFn *fn_entry);
-Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent);
-Scope *create_nosuspend_scope(CodeGen *g, AstNode *node, Scope *parent);
-Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstSrc *is_comptime);
-Scope *create_typeof_scope(CodeGen *g, AstNode *node, Scope *parent);
-ScopeExpr *create_expr_scope(CodeGen *g, AstNode *node, Scope *parent);
-
-void init_const_str_lit(CodeGen *g, ZigValue *const_val, Buf *str);
-ZigValue *create_const_str_lit(CodeGen *g, Buf *str);
-
-void init_const_bigint(ZigValue *const_val, ZigType *type, const BigInt *bigint);
-ZigValue *create_const_bigint(CodeGen *g, ZigType *type, const BigInt *bigint);
-
-void init_const_unsigned_negative(ZigValue *const_val, ZigType *type, uint64_t x, bool negative);
-ZigValue *create_const_unsigned_negative(CodeGen *g, ZigType *type, uint64_t x, bool negative);
-
-void init_const_signed(ZigValue *const_val, ZigType *type, int64_t x);
-ZigValue *create_const_signed(CodeGen *g, ZigType *type, int64_t x);
-
-void init_const_usize(CodeGen *g, ZigValue *const_val, uint64_t x);
-ZigValue *create_const_usize(CodeGen *g, uint64_t x);
-
-void init_const_float(ZigValue *const_val, ZigType *type, double value);
-ZigValue *create_const_float(CodeGen *g, ZigType *type, double value);
-
-void init_const_enum(ZigValue *const_val, ZigType *type, const BigInt *tag);
-ZigValue *create_const_enum(CodeGen *g, ZigType *type, const BigInt *tag);
-
-void init_const_bool(CodeGen *g, ZigValue *const_val, bool value);
-ZigValue *create_const_bool(CodeGen *g, bool value);
-
-void init_const_type(CodeGen *g, ZigValue *const_val, ZigType *type_value);
-ZigValue *create_const_type(CodeGen *g, ZigType *type_value);
-
-void init_const_runtime(ZigValue *const_val, ZigType *type);
-ZigValue *create_const_runtime(CodeGen *g, ZigType *type);
-
-void init_const_ptr_ref(CodeGen *g, ZigValue *const_val, ZigValue *pointee_val, bool is_const);
-ZigValue *create_const_ptr_ref(CodeGen *g, ZigValue *pointee_val, bool is_const);
-
-void init_const_ptr_hard_coded_addr(CodeGen *g, ZigValue *const_val, ZigType *pointee_type,
- size_t addr, bool is_const);
-ZigValue *create_const_ptr_hard_coded_addr(CodeGen *g, ZigType *pointee_type,
- size_t addr, bool is_const);
-
-void init_const_ptr_array(CodeGen *g, ZigValue *const_val, ZigValue *array_val,
- size_t elem_index, bool is_const, PtrLen ptr_len);
-ZigValue *create_const_ptr_array(CodeGen *g, ZigValue *array_val, size_t elem_index,
- bool is_const, PtrLen ptr_len);
-
-void init_const_slice(CodeGen *g, ZigValue *const_val, ZigValue *array_val,
- size_t start, size_t len, bool is_const);
-ZigValue *create_const_slice(CodeGen *g, ZigValue *array_val, size_t start, size_t len, bool is_const);
-
-void init_const_null(ZigValue *const_val, ZigType *type);
-ZigValue *create_const_null(CodeGen *g, ZigType *type);
-
-void init_const_fn(ZigValue *const_val, ZigFn *fn);
-ZigValue *create_const_fn(CodeGen *g, ZigFn *fn);
-
-ZigValue **alloc_const_vals_ptrs(CodeGen *g, size_t count);
-ZigValue **realloc_const_vals_ptrs(CodeGen *g, ZigValue **ptr, size_t old_count, size_t new_count);
-
-TypeStructField **alloc_type_struct_fields(size_t count);
-TypeStructField **realloc_type_struct_fields(TypeStructField **ptr, size_t old_count, size_t new_count);
-
-ZigType *make_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits);
-void expand_undef_array(CodeGen *g, ZigValue *const_val);
-void expand_undef_struct(CodeGen *g, ZigValue *const_val);
-void update_compile_var(CodeGen *g, Buf *name, ZigValue *value);
-
-const char *type_id_name(ZigTypeId id);
-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);
-ZigType *get_align_amt_type(CodeGen *g);
-ZigPackage *new_anonymous_package(void);
-
-Buf *const_value_to_buffer(ZigValue *const_val);
-void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, CallingConvention cc);
-void add_var_export(CodeGen *g, ZigVar *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage);
-
-
-ZigValue *get_builtin_value(CodeGen *codegen, const char *name);
-ZigType *get_builtin_type(CodeGen *codegen, const char *name);
-ZigType *get_stack_trace_type(CodeGen *g);
-bool resolve_inferred_error_set(CodeGen *g, ZigType *err_set_type, AstNode *source_node);
-
-ZigType *get_auto_err_set_type(CodeGen *g, ZigFn *fn_entry);
-
-bool fn_type_can_fail(FnTypeId *fn_type_id);
-bool type_can_fail(ZigType *type_entry);
-bool fn_eval_cacheable(Scope *scope, ZigType *return_type);
-AstNode *type_decl_node(ZigType *type_entry);
-
-Error get_primitive_type(CodeGen *g, Buf *name, ZigType **result);
-
-bool calling_convention_allows_zig_types(CallingConvention cc);
-const char *calling_convention_name(CallingConvention cc);
-
-Error ATTRIBUTE_MUST_USE file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents);
-
-void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk);
-X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty);
-bool type_is_c_abi_int_bail(CodeGen *g, ZigType *ty);
-Error type_is_c_abi_int(CodeGen *g, ZigType *ty, bool *result);
-bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id);
-const char *container_string(ContainerKind kind);
-
-uint32_t get_host_int_bytes(CodeGen *g, ZigType *struct_type, TypeStructField *field);
-
-enum ReqCompTime {
- ReqCompTimeInvalid,
- ReqCompTimeNo,
- ReqCompTimeYes,
-};
-ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry);
-
-OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry);
-
-Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node,
- ZigValue *const_val, ZigType *wanted_type);
-
-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);
-
-void add_cc_args(CodeGen *g, ZigList &args, const char *out_dep_path, bool translate_c,
- FileExt source_kind);
-
-void src_assert_impl(bool ok, AstNode *source_node, const char *file, unsigned int line);
-bool is_container(ZigType *type_entry);
-ZigValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry,
- Buf *type_name, UndefAllowed undef);
-
-void resolve_llvm_types_fn(CodeGen *g, ZigFn *fn);
-bool fn_is_async(ZigFn *fn);
-CallingConvention cc_from_fn_proto(AstNodeFnProto *fn_proto);
-bool is_valid_return_type(ZigType* type);
-bool is_valid_param_type(ZigType* type);
-
-Error type_val_resolve_abi_align(CodeGen *g, AstNode *source_node, ZigValue *type_val, uint32_t *abi_align);
-Error type_val_resolve_abi_size(CodeGen *g, AstNode *source_node, ZigValue *type_val,
- size_t *abi_size, size_t *size_in_bits);
-Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent_type,
- ZigValue *parent_type_val, bool *is_zero_bits);
-ZigType *resolve_union_field_type(CodeGen *g, TypeUnionField *union_field);
-ZigType *resolve_struct_field_type(CodeGen *g, TypeStructField *struct_field);
-
-void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn);
-
-Error analyze_import(CodeGen *codegen, ZigType *source_import, Buf *import_target_str,
- ZigType **out_import, Buf **out_import_target_path, Buf *out_full_path);
-ZigValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry);
-bool is_anon_container(ZigType *ty);
-void copy_const_val(CodeGen *g, ZigValue *dest, ZigValue *src);
-bool type_has_optional_repr(ZigType *ty);
-bool is_opt_err_set(ZigType *ty);
-bool type_is_numeric(ZigType *ty);
-const char *float_op_to_name(BuiltinFnId op);
-
-#define src_assert(OK, SOURCE_NODE) src_assert_impl((OK), (SOURCE_NODE), __FILE__, __LINE__)
-
-#endif
diff --git a/src/ast_render.cpp b/src/ast_render.cpp
deleted file mode 100644
index ad308bf416a900d791892d94dbb3d09bb6fa0039..0000000000000000000000000000000000000000
--- a/src/ast_render.cpp
+++ /dev/null
@@ -1,1246 +0,0 @@
-/*
- * Copyright (c) 2016 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#include "analyze.hpp"
-#include "ast_render.hpp"
-#include "os.hpp"
-
-#include
-
-static const char *bin_op_str(BinOpType bin_op) {
- switch (bin_op) {
- case BinOpTypeInvalid: return "(invalid)";
- case BinOpTypeBoolOr: return "or";
- case BinOpTypeBoolAnd: return "and";
- case BinOpTypeCmpEq: return "==";
- case BinOpTypeCmpNotEq: return "!=";
- case BinOpTypeCmpLessThan: return "<";
- case BinOpTypeCmpGreaterThan: return ">";
- case BinOpTypeCmpLessOrEq: return "<=";
- case BinOpTypeCmpGreaterOrEq: return ">=";
- case BinOpTypeBinOr: return "|";
- case BinOpTypeBinXor: return "^";
- case BinOpTypeBinAnd: return "&";
- case BinOpTypeBitShiftLeft: return "<<";
- case BinOpTypeBitShiftRight: return ">>";
- case BinOpTypeAdd: return "+";
- case BinOpTypeAddWrap: return "+%";
- case BinOpTypeSub: return "-";
- case BinOpTypeSubWrap: return "-%";
- case BinOpTypeMult: return "*";
- case BinOpTypeMultWrap: return "*%";
- case BinOpTypeDiv: return "/";
- case BinOpTypeMod: return "%";
- case BinOpTypeAssign: return "=";
- case BinOpTypeAssignTimes: return "*=";
- case BinOpTypeAssignTimesWrap: return "*%=";
- case BinOpTypeAssignDiv: return "/=";
- case BinOpTypeAssignMod: return "%=";
- case BinOpTypeAssignPlus: return "+=";
- case BinOpTypeAssignPlusWrap: return "+%=";
- case BinOpTypeAssignMinus: return "-=";
- case BinOpTypeAssignMinusWrap: return "-%=";
- case BinOpTypeAssignBitShiftLeft: return "<<=";
- case BinOpTypeAssignBitShiftRight: return ">>=";
- case BinOpTypeAssignBitAnd: return "&=";
- case BinOpTypeAssignBitXor: return "^=";
- case BinOpTypeAssignBitOr: return "|=";
- case BinOpTypeAssignMergeErrorSets: return "||=";
- case BinOpTypeUnwrapOptional: return "orelse";
- case BinOpTypeArrayCat: return "++";
- case BinOpTypeArrayMult: return "**";
- case BinOpTypeErrorUnion: return "!";
- case BinOpTypeMergeErrorSets: return "||";
- }
- zig_unreachable();
-}
-
-static const char *prefix_op_str(PrefixOp prefix_op) {
- switch (prefix_op) {
- case PrefixOpInvalid: return "(invalid)";
- case PrefixOpNegation: return "-";
- case PrefixOpNegationWrap: return "-%";
- case PrefixOpBoolNot: return "!";
- case PrefixOpBinNot: return "~";
- case PrefixOpOptional: return "?";
- case PrefixOpAddrOf: return "&";
- }
- zig_unreachable();
-}
-
-static const char *visib_mod_string(VisibMod mod) {
- switch (mod) {
- case VisibModPub: return "pub ";
- case VisibModPrivate: return "";
- }
- zig_unreachable();
-}
-
-static const char *return_string(ReturnKind kind) {
- switch (kind) {
- case ReturnKindUnconditional: return "return";
- case ReturnKindError: return "try";
- }
- zig_unreachable();
-}
-
-static const char *defer_string(ReturnKind kind) {
- switch (kind) {
- case ReturnKindUnconditional: return "defer";
- case ReturnKindError: return "errdefer";
- }
- zig_unreachable();
-}
-
-static const char *layout_string(ContainerLayout layout) {
- switch (layout) {
- case ContainerLayoutAuto: return "";
- case ContainerLayoutExtern: return "extern ";
- case ContainerLayoutPacked: return "packed ";
- }
- zig_unreachable();
-}
-
-static const char *extern_string(bool is_extern) {
- return is_extern ? "extern " : "";
-}
-
-static const char *export_string(bool is_export) {
- return is_export ? "export " : "";
-}
-
-//static const char *calling_convention_string(CallingConvention cc) {
-// switch (cc) {
-// case CallingConventionUnspecified: return "";
-// case CallingConventionC: return "extern ";
-// case CallingConventionCold: return "coldcc ";
-// case CallingConventionNaked: return "nakedcc ";
-// case CallingConventionStdcall: return "stdcallcc ";
-// }
-// zig_unreachable();
-//}
-
-static const char *inline_string(FnInline fn_inline) {
- switch (fn_inline) {
- case FnInlineAlways: return "inline ";
- case FnInlineNever: return "noinline ";
- case FnInlineAuto: return "";
- }
- zig_unreachable();
-}
-
-static const char *const_or_var_string(bool is_const) {
- return is_const ? "const" : "var";
-}
-
-static const char *thread_local_string(Token *tok) {
- return (tok == nullptr) ? "" : "threadlocal ";
-}
-
-static const char *token_to_ptr_len_str(Token *tok) {
- assert(tok != nullptr);
- switch (tok->id) {
- case TokenIdStar:
- case TokenIdStarStar:
- return "*";
- case TokenIdLBracket:
- return "[*]";
- case TokenIdSymbol:
- return "[*c]";
- default:
- zig_unreachable();
- }
-}
-
-static const char *node_type_str(NodeType node_type) {
- switch (node_type) {
- case NodeTypeFnDef:
- return "FnDef";
- case NodeTypeFnProto:
- return "FnProto";
- case NodeTypeParamDecl:
- return "ParamDecl";
- case NodeTypeBlock:
- return "Block";
- case NodeTypeGroupedExpr:
- return "Parens";
- case NodeTypeBinOpExpr:
- return "BinOpExpr";
- case NodeTypeCatchExpr:
- return "CatchExpr";
- case NodeTypeFnCallExpr:
- return "FnCallExpr";
- case NodeTypeArrayAccessExpr:
- return "ArrayAccessExpr";
- case NodeTypeSliceExpr:
- return "SliceExpr";
- case NodeTypeReturnExpr:
- return "ReturnExpr";
- case NodeTypeDefer:
- return "Defer";
- case NodeTypeVariableDeclaration:
- return "VariableDeclaration";
- case NodeTypeTestDecl:
- return "TestDecl";
- case NodeTypeIntLiteral:
- return "IntLiteral";
- case NodeTypeFloatLiteral:
- return "FloatLiteral";
- case NodeTypeStringLiteral:
- return "StringLiteral";
- case NodeTypeCharLiteral:
- return "CharLiteral";
- case NodeTypeSymbol:
- return "Symbol";
- case NodeTypePrefixOpExpr:
- return "PrefixOpExpr";
- case NodeTypeUsingNamespace:
- return "UsingNamespace";
- case NodeTypeBoolLiteral:
- return "BoolLiteral";
- case NodeTypeNullLiteral:
- return "NullLiteral";
- case NodeTypeUndefinedLiteral:
- return "UndefinedLiteral";
- case NodeTypeIfBoolExpr:
- return "IfBoolExpr";
- case NodeTypeWhileExpr:
- return "WhileExpr";
- case NodeTypeForExpr:
- return "ForExpr";
- case NodeTypeSwitchExpr:
- return "SwitchExpr";
- case NodeTypeSwitchProng:
- return "SwitchProng";
- case NodeTypeSwitchRange:
- return "SwitchRange";
- case NodeTypeCompTime:
- return "CompTime";
- case NodeTypeNoSuspend:
- return "NoSuspend";
- case NodeTypeBreak:
- return "Break";
- case NodeTypeContinue:
- return "Continue";
- case NodeTypeUnreachable:
- return "Unreachable";
- case NodeTypeAsmExpr:
- return "AsmExpr";
- case NodeTypeFieldAccessExpr:
- return "FieldAccessExpr";
- case NodeTypePtrDeref:
- return "PtrDerefExpr";
- case NodeTypeUnwrapOptional:
- return "UnwrapOptional";
- case NodeTypeContainerDecl:
- return "ContainerDecl";
- case NodeTypeStructField:
- return "StructField";
- case NodeTypeStructValueField:
- return "StructValueField";
- case NodeTypeContainerInitExpr:
- return "ContainerInitExpr";
- case NodeTypeArrayType:
- return "ArrayType";
- case NodeTypeInferredArrayType:
- return "InferredArrayType";
- case NodeTypeErrorType:
- return "ErrorType";
- case NodeTypeIfErrorExpr:
- return "IfErrorExpr";
- case NodeTypeIfOptional:
- return "IfOptional";
- case NodeTypeErrorSetDecl:
- return "ErrorSetDecl";
- case NodeTypeResume:
- return "Resume";
- case NodeTypeAwaitExpr:
- return "AwaitExpr";
- case NodeTypeSuspend:
- return "Suspend";
- case NodeTypePointerType:
- return "PointerType";
- case NodeTypeAnyFrameType:
- return "AnyFrameType";
- case NodeTypeEnumLiteral:
- return "EnumLiteral";
- case NodeTypeErrorSetField:
- return "ErrorSetField";
- case NodeTypeAnyTypeField:
- return "AnyTypeField";
- }
- zig_unreachable();
-}
-
-struct AstPrint {
- int indent;
- FILE *f;
-};
-
-static void ast_print_visit(AstNode **node_ptr, void *context) {
- AstNode *node = *node_ptr;
- AstPrint *ap = (AstPrint *)context;
-
- for (int i = 0; i < ap->indent; i += 1) {
- fprintf(ap->f, " ");
- }
-
- fprintf(ap->f, "%s\n", node_type_str(node->type));
-
- AstPrint new_ap;
- new_ap.indent = ap->indent + 2;
- new_ap.f = ap->f;
-
- ast_visit_node_children(node, ast_print_visit, &new_ap);
-}
-
-void ast_print(FILE *f, AstNode *node, int indent) {
- AstPrint ap;
- ap.indent = indent;
- ap.f = f;
- ast_visit_node_children(node, ast_print_visit, &ap);
-}
-
-
-struct AstRender {
- int indent;
- int indent_size;
- FILE *f;
-};
-
-static void print_indent(AstRender *ar) {
- for (int i = 0; i < ar->indent; i += 1) {
- fprintf(ar->f, " ");
- }
-}
-
-static bool is_alpha_under(uint8_t c) {
- return (c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') || c == '_';
-}
-
-static bool is_digit(uint8_t c) {
- return (c >= '0' && c <= '9');
-}
-
-static bool is_printable(uint8_t c) {
- if (c == 0) {
- return false;
- }
- static const uint8_t printables[] =
- " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.~`!@#$%^&*()_-+=\\{}[];'\"?/<>,:";
- for (size_t i = 0; i < array_length(printables); i += 1) {
- if (c == printables[i]) return true;
- }
- return false;
-}
-
-static void string_literal_escape(Buf *source, Buf *dest) {
- buf_resize(dest, 0);
- for (size_t i = 0; i < buf_len(source); i += 1) {
- uint8_t c = *((uint8_t*)buf_ptr(source) + i);
- if (c == '\'') {
- buf_append_str(dest, "\\'");
- } else if (c == '"') {
- buf_append_str(dest, "\\\"");
- } else if (c == '\\') {
- buf_append_str(dest, "\\\\");
- } else if (c == '\n') {
- buf_append_str(dest, "\\n");
- } else if (c == '\r') {
- buf_append_str(dest, "\\r");
- } else if (c == '\t') {
- buf_append_str(dest, "\\t");
- } else if (is_printable(c)) {
- buf_append_char(dest, c);
- } else {
- buf_appendf(dest, "\\x%02x", (int)c);
- }
- }
-}
-
-static bool is_valid_bare_symbol(Buf *symbol) {
- if (buf_len(symbol) == 0) {
- return false;
- }
- uint8_t first_char = *buf_ptr(symbol);
- if (!is_alpha_under(first_char)) {
- return false;
- }
- for (size_t i = 1; i < buf_len(symbol); i += 1) {
- uint8_t c = *((uint8_t*)buf_ptr(symbol) + i);
- if (!is_alpha_under(c) && !is_digit(c)) {
- return false;
- }
- }
- return true;
-}
-
-static void print_symbol(AstRender *ar, Buf *symbol) {
- if (is_zig_keyword(symbol)) {
- fprintf(ar->f, "@\"%s\"", buf_ptr(symbol));
- return;
- }
- if (is_valid_bare_symbol(symbol)) {
- fprintf(ar->f, "%s", buf_ptr(symbol));
- return;
- }
- Buf escaped = BUF_INIT;
- string_literal_escape(symbol, &escaped);
- fprintf(ar->f, "@\"%s\"", buf_ptr(&escaped));
-}
-
-static bool statement_terminates_without_semicolon(AstNode *node) {
- switch (node->type) {
- case NodeTypeIfBoolExpr:
- if (node->data.if_bool_expr.else_node)
- return statement_terminates_without_semicolon(node->data.if_bool_expr.else_node);
- return node->data.if_bool_expr.then_block->type == NodeTypeBlock;
- case NodeTypeIfErrorExpr:
- if (node->data.if_err_expr.else_node)
- return statement_terminates_without_semicolon(node->data.if_err_expr.else_node);
- return node->data.if_err_expr.then_node->type == NodeTypeBlock;
- case NodeTypeIfOptional:
- if (node->data.test_expr.else_node)
- return statement_terminates_without_semicolon(node->data.test_expr.else_node);
- return node->data.test_expr.then_node->type == NodeTypeBlock;
- case NodeTypeWhileExpr:
- return node->data.while_expr.body->type == NodeTypeBlock;
- case NodeTypeForExpr:
- return node->data.for_expr.body->type == NodeTypeBlock;
- case NodeTypeCompTime:
- return node->data.comptime_expr.expr->type == NodeTypeBlock;
- case NodeTypeDefer:
- return node->data.defer.expr->type == NodeTypeBlock;
- case NodeTypeSuspend:
- return node->data.suspend.block != nullptr && node->data.suspend.block->type == NodeTypeBlock;
- case NodeTypeSwitchExpr:
- case NodeTypeBlock:
- return true;
- default:
- return false;
- }
-}
-
-static void render_node_extra(AstRender *ar, AstNode *node, bool grouped);
-
-static void render_node_grouped(AstRender *ar, AstNode *node) {
- return render_node_extra(ar, node, true);
-}
-
-static void render_node_ungrouped(AstRender *ar, AstNode *node) {
- return render_node_extra(ar, node, false);
-}
-
-static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
- switch (node->type) {
- case NodeTypeSwitchProng:
- case NodeTypeSwitchRange:
- case NodeTypeStructValueField:
- zig_unreachable();
- case NodeTypeFnProto:
- {
- const char *pub_str = visib_mod_string(node->data.fn_proto.visib_mod);
- const char *extern_str = extern_string(node->data.fn_proto.is_extern);
- const char *export_str = export_string(node->data.fn_proto.is_export);
- const char *inline_str = inline_string(node->data.fn_proto.fn_inline);
- fprintf(ar->f, "%s%s%s%sfn ", pub_str, inline_str, export_str, extern_str);
- if (node->data.fn_proto.name != nullptr) {
- print_symbol(ar, node->data.fn_proto.name);
- }
- fprintf(ar->f, "(");
- size_t arg_count = node->data.fn_proto.params.length;
- for (size_t arg_i = 0; arg_i < arg_count; arg_i += 1) {
- AstNode *param_decl = node->data.fn_proto.params.at(arg_i);
- assert(param_decl->type == NodeTypeParamDecl);
- if (param_decl->data.param_decl.name != nullptr) {
- const char *noalias_str = param_decl->data.param_decl.is_noalias ? "noalias " : "";
- const char *inline_str = param_decl->data.param_decl.is_comptime ? "comptime " : "";
- fprintf(ar->f, "%s%s", noalias_str, inline_str);
- print_symbol(ar, param_decl->data.param_decl.name);
- fprintf(ar->f, ": ");
- }
- if (param_decl->data.param_decl.is_var_args) {
- fprintf(ar->f, "...");
- } else if (param_decl->data.param_decl.anytype_token != nullptr) {
- fprintf(ar->f, "anytype");
- } else {
- render_node_grouped(ar, param_decl->data.param_decl.type);
- }
-
- if (arg_i + 1 < arg_count) {
- fprintf(ar->f, ", ");
- }
- }
- if (node->data.fn_proto.is_var_args) {
- fprintf(ar->f, ", ...");
- }
- fprintf(ar->f, ")");
- if (node->data.fn_proto.align_expr) {
- fprintf(ar->f, " align(");
- render_node_grouped(ar, node->data.fn_proto.align_expr);
- fprintf(ar->f, ")");
- }
- if (node->data.fn_proto.section_expr) {
- fprintf(ar->f, " section(");
- render_node_grouped(ar, node->data.fn_proto.section_expr);
- fprintf(ar->f, ")");
- }
- if (node->data.fn_proto.callconv_expr) {
- fprintf(ar->f, " callconv(");
- render_node_grouped(ar, node->data.fn_proto.callconv_expr);
- fprintf(ar->f, ")");
- }
-
- if (node->data.fn_proto.return_anytype_token != nullptr) {
- fprintf(ar->f, "anytype");
- } else {
- AstNode *return_type_node = node->data.fn_proto.return_type;
- assert(return_type_node != nullptr);
- fprintf(ar->f, " ");
- if (node->data.fn_proto.auto_err_set) {
- fprintf(ar->f, "!");
- }
- render_node_grouped(ar, return_type_node);
- }
- break;
- }
- case NodeTypeFnDef:
- {
- render_node_grouped(ar, node->data.fn_def.fn_proto);
- fprintf(ar->f, " ");
- render_node_grouped(ar, node->data.fn_def.body);
- break;
- }
- case NodeTypeBlock:
- if (node->data.block.name != nullptr) {
- fprintf(ar->f, "%s: ", buf_ptr(node->data.block.name));
- }
- if (node->data.block.statements.length == 0) {
- fprintf(ar->f, "{}");
- break;
- }
- fprintf(ar->f, "{\n");
- ar->indent += ar->indent_size;
- for (size_t i = 0; i < node->data.block.statements.length; i += 1) {
- AstNode *statement = node->data.block.statements.at(i);
- print_indent(ar);
- render_node_grouped(ar, statement);
-
- if (!statement_terminates_without_semicolon(statement))
- fprintf(ar->f, ";");
-
- fprintf(ar->f, "\n");
- }
- ar->indent -= ar->indent_size;
- print_indent(ar);
- fprintf(ar->f, "}");
- break;
- case NodeTypeGroupedExpr:
- fprintf(ar->f, "(");
- render_node_ungrouped(ar, node->data.grouped_expr);
- fprintf(ar->f, ")");
- break;
- case NodeTypeReturnExpr:
- {
- const char *return_str = return_string(node->data.return_expr.kind);
- fprintf(ar->f, "%s", return_str);
- if (node->data.return_expr.expr) {
- fprintf(ar->f, " ");
- render_node_grouped(ar, node->data.return_expr.expr);
- }
- break;
- }
- case NodeTypeBreak:
- {
- fprintf(ar->f, "break");
- if (node->data.break_expr.name != nullptr) {
- fprintf(ar->f, " :%s", buf_ptr(node->data.break_expr.name));
- }
- if (node->data.break_expr.expr) {
- fprintf(ar->f, " ");
- render_node_grouped(ar, node->data.break_expr.expr);
- }
- break;
- }
- case NodeTypeDefer:
- {
- const char *defer_str = defer_string(node->data.defer.kind);
- fprintf(ar->f, "%s ", defer_str);
- render_node_grouped(ar, node->data.defer.expr);
- break;
- }
- case NodeTypeVariableDeclaration:
- {
- const char *pub_str = visib_mod_string(node->data.variable_declaration.visib_mod);
- const char *extern_str = extern_string(node->data.variable_declaration.is_extern);
- const char *thread_local_str = thread_local_string(node->data.variable_declaration.threadlocal_tok);
- const char *const_or_var = const_or_var_string(node->data.variable_declaration.is_const);
- fprintf(ar->f, "%s%s%s%s ", pub_str, extern_str, thread_local_str, const_or_var);
- print_symbol(ar, node->data.variable_declaration.symbol);
-
- if (node->data.variable_declaration.type) {
- fprintf(ar->f, ": ");
- render_node_grouped(ar, node->data.variable_declaration.type);
- }
- if (node->data.variable_declaration.align_expr) {
- fprintf(ar->f, "align(");
- render_node_grouped(ar, node->data.variable_declaration.align_expr);
- fprintf(ar->f, ") ");
- }
- if (node->data.variable_declaration.section_expr) {
- fprintf(ar->f, "section(");
- render_node_grouped(ar, node->data.variable_declaration.section_expr);
- fprintf(ar->f, ") ");
- }
- if (node->data.variable_declaration.expr) {
- fprintf(ar->f, " = ");
- render_node_grouped(ar, node->data.variable_declaration.expr);
- }
- break;
- }
- case NodeTypeBinOpExpr:
- if (!grouped) fprintf(ar->f, "(");
- render_node_ungrouped(ar, node->data.bin_op_expr.op1);
- fprintf(ar->f, " %s ", bin_op_str(node->data.bin_op_expr.bin_op));
- render_node_ungrouped(ar, node->data.bin_op_expr.op2);
- if (!grouped) fprintf(ar->f, ")");
- break;
- case NodeTypeFloatLiteral:
- {
- Buf rendered_buf = BUF_INIT;
- buf_resize(&rendered_buf, 0);
- bigfloat_append_buf(&rendered_buf, node->data.float_literal.bigfloat);
- fprintf(ar->f, "%s", buf_ptr(&rendered_buf));
- }
- break;
- case NodeTypeIntLiteral:
- {
- Buf rendered_buf = BUF_INIT;
- buf_resize(&rendered_buf, 0);
- bigint_append_buf(&rendered_buf, node->data.int_literal.bigint, 10);
- fprintf(ar->f, "%s", buf_ptr(&rendered_buf));
- }
- break;
- case NodeTypeStringLiteral:
- {
- Buf tmp_buf = BUF_INIT;
- string_literal_escape(node->data.string_literal.buf, &tmp_buf);
- fprintf(ar->f, "\"%s\"", buf_ptr(&tmp_buf));
- }
- break;
- case NodeTypeCharLiteral:
- {
- uint8_t c = node->data.char_literal.value;
- if (c == '\'') {
- fprintf(ar->f, "'\\''");
- } else if (c == '\"') {
- fprintf(ar->f, "'\\\"'");
- } else if (c == '\\') {
- fprintf(ar->f, "'\\\\'");
- } else if (c == '\n') {
- fprintf(ar->f, "'\\n'");
- } else if (c == '\r') {
- fprintf(ar->f, "'\\r'");
- } else if (c == '\t') {
- fprintf(ar->f, "'\\t'");
- } else if (is_printable(c)) {
- fprintf(ar->f, "'%c'", c);
- } else {
- fprintf(ar->f, "'\\x%02x'", (int)c);
- }
- break;
- }
- case NodeTypeSymbol:
- print_symbol(ar, node->data.symbol_expr.symbol);
- break;
- case NodeTypePrefixOpExpr:
- {
- if (!grouped) fprintf(ar->f, "(");
- PrefixOp op = node->data.prefix_op_expr.prefix_op;
- fprintf(ar->f, "%s", prefix_op_str(op));
-
- AstNode *child_node = node->data.prefix_op_expr.primary_expr;
- bool new_grouped = child_node->type == NodeTypePrefixOpExpr || child_node->type == NodeTypePointerType;
- render_node_extra(ar, child_node, new_grouped);
- if (!grouped) fprintf(ar->f, ")");
- break;
- }
- case NodeTypePointerType:
- {
- if (!grouped) fprintf(ar->f, "(");
- const char *ptr_len_str = token_to_ptr_len_str(node->data.pointer_type.star_token);
- fprintf(ar->f, "%s", ptr_len_str);
- if (node->data.pointer_type.align_expr != nullptr) {
- fprintf(ar->f, "align(");
- render_node_grouped(ar, node->data.pointer_type.align_expr);
- if (node->data.pointer_type.bit_offset_start != nullptr) {
- assert(node->data.pointer_type.host_int_bytes != nullptr);
-
- Buf offset_start_buf = BUF_INIT;
- buf_resize(&offset_start_buf, 0);
- bigint_append_buf(&offset_start_buf, node->data.pointer_type.bit_offset_start, 10);
-
- Buf offset_end_buf = BUF_INIT;
- buf_resize(&offset_end_buf, 0);
- bigint_append_buf(&offset_end_buf, node->data.pointer_type.host_int_bytes, 10);
-
- fprintf(ar->f, ":%s:%s ", buf_ptr(&offset_start_buf), buf_ptr(&offset_end_buf));
- }
- fprintf(ar->f, ") ");
- }
- if (node->data.pointer_type.is_const) {
- fprintf(ar->f, "const ");
- }
- if (node->data.pointer_type.is_volatile) {
- fprintf(ar->f, "volatile ");
- }
-
- render_node_ungrouped(ar, node->data.pointer_type.op_expr);
- if (!grouped) fprintf(ar->f, ")");
- break;
- }
- case NodeTypeFnCallExpr:
- {
- switch (node->data.fn_call_expr.modifier) {
- case CallModifierNone:
- break;
- case CallModifierNoSuspend:
- fprintf(ar->f, "nosuspend ");
- break;
- case CallModifierAsync:
- fprintf(ar->f, "async ");
- break;
- case CallModifierNeverTail:
- fprintf(ar->f, "notail ");
- break;
- case CallModifierNeverInline:
- fprintf(ar->f, "noinline ");
- break;
- case CallModifierAlwaysTail:
- fprintf(ar->f, "tail ");
- break;
- case CallModifierAlwaysInline:
- fprintf(ar->f, "inline ");
- break;
- case CallModifierCompileTime:
- fprintf(ar->f, "comptime ");
- break;
- case CallModifierBuiltin:
- fprintf(ar->f, "@");
- break;
- }
- AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
- bool grouped = (fn_ref_node->type != NodeTypePrefixOpExpr && fn_ref_node->type != NodeTypePointerType);
- render_node_extra(ar, fn_ref_node, grouped);
- fprintf(ar->f, "(");
- for (size_t i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
- AstNode *param = node->data.fn_call_expr.params.at(i);
- if (i != 0) {
- fprintf(ar->f, ", ");
- }
- render_node_grouped(ar, param);
- }
- fprintf(ar->f, ")");
- break;
- }
- case NodeTypeArrayAccessExpr:
- render_node_ungrouped(ar, node->data.array_access_expr.array_ref_expr);
- fprintf(ar->f, "[");
- render_node_grouped(ar, node->data.array_access_expr.subscript);
- fprintf(ar->f, "]");
- break;
- case NodeTypeFieldAccessExpr:
- {
- AstNode *lhs = node->data.field_access_expr.struct_expr;
- Buf *rhs = node->data.field_access_expr.field_name;
- if (lhs->type == NodeTypeErrorType) {
- fprintf(ar->f, "error");
- } else {
- render_node_ungrouped(ar, lhs);
- }
- fprintf(ar->f, ".");
- print_symbol(ar, rhs);
- break;
- }
- case NodeTypePtrDeref:
- {
- AstNode *lhs = node->data.ptr_deref_expr.target;
- render_node_ungrouped(ar, lhs);
- fprintf(ar->f, ".*");
- break;
- }
- case NodeTypeUnwrapOptional:
- {
- AstNode *lhs = node->data.unwrap_optional.expr;
- render_node_ungrouped(ar, lhs);
- fprintf(ar->f, ".?");
- break;
- }
- case NodeTypeUndefinedLiteral:
- fprintf(ar->f, "undefined");
- break;
- case NodeTypeContainerDecl:
- {
- if (!node->data.container_decl.is_root) {
- const char *layout_str = layout_string(node->data.container_decl.layout);
- const char *container_str = container_string(node->data.container_decl.kind);
- fprintf(ar->f, "%s%s", layout_str, container_str);
- if (node->data.container_decl.auto_enum) {
- fprintf(ar->f, "(enum");
- }
- if (node->data.container_decl.init_arg_expr != nullptr) {
- fprintf(ar->f, "(");
- render_node_grouped(ar, node->data.container_decl.init_arg_expr);
- fprintf(ar->f, ")");
- }
- if (node->data.container_decl.auto_enum) {
- fprintf(ar->f, ")");
- }
-
- fprintf(ar->f, " {\n");
- ar->indent += ar->indent_size;
- }
- for (size_t field_i = 0; field_i < node->data.container_decl.fields.length; field_i += 1) {
- AstNode *field_node = node->data.container_decl.fields.at(field_i);
- assert(field_node->type == NodeTypeStructField);
- print_indent(ar);
- print_symbol(ar, field_node->data.struct_field.name);
- if (field_node->data.struct_field.type != nullptr) {
- fprintf(ar->f, ": ");
- render_node_grouped(ar, field_node->data.struct_field.type);
- }
- if (field_node->data.struct_field.value != nullptr) {
- fprintf(ar->f, " = ");
- render_node_grouped(ar, field_node->data.struct_field.value);
- }
- fprintf(ar->f, ",\n");
- }
-
- for (size_t decl_i = 0; decl_i < node->data.container_decl.decls.length; decl_i += 1) {
- AstNode *decls_node = node->data.container_decl.decls.at(decl_i);
- render_node_grouped(ar, decls_node);
-
- if (decls_node->type == NodeTypeUsingNamespace ||
- decls_node->type == NodeTypeVariableDeclaration ||
- decls_node->type == NodeTypeFnProto)
- {
- fprintf(ar->f, ";");
- }
- fprintf(ar->f, "\n");
- }
-
- if (!node->data.container_decl.is_root) {
- ar->indent -= ar->indent_size;
- print_indent(ar);
- fprintf(ar->f, "}");
- }
- break;
- }
- case NodeTypeContainerInitExpr:
- if (node->data.container_init_expr.type != nullptr) {
- render_node_ungrouped(ar, node->data.container_init_expr.type);
- }
- if (node->data.container_init_expr.kind == ContainerInitKindStruct) {
- fprintf(ar->f, "{\n");
- ar->indent += ar->indent_size;
- } else {
- fprintf(ar->f, "{");
- }
- for (size_t i = 0; i < node->data.container_init_expr.entries.length; i += 1) {
- AstNode *entry = node->data.container_init_expr.entries.at(i);
- if (entry->type == NodeTypeStructValueField) {
- Buf *name = entry->data.struct_val_field.name;
- AstNode *expr = entry->data.struct_val_field.expr;
- print_indent(ar);
- fprintf(ar->f, ".%s = ", buf_ptr(name));
- render_node_grouped(ar, expr);
- fprintf(ar->f, ",\n");
- } else {
- if (i != 0)
- fprintf(ar->f, ", ");
- render_node_grouped(ar, entry);
- }
- }
- if (node->data.container_init_expr.kind == ContainerInitKindStruct) {
- ar->indent -= ar->indent_size;
- }
- print_indent(ar);
- fprintf(ar->f, "}");
- break;
- case NodeTypeArrayType:
- {
- fprintf(ar->f, "[");
- if (node->data.array_type.size) {
- render_node_grouped(ar, node->data.array_type.size);
- }
- fprintf(ar->f, "]");
- if (node->data.array_type.is_const) {
- fprintf(ar->f, "const ");
- }
- render_node_ungrouped(ar, node->data.array_type.child_type);
- break;
- }
- case NodeTypeInferredArrayType:
- {
- fprintf(ar->f, "[_]");
- render_node_ungrouped(ar, node->data.inferred_array_type.child_type);
- break;
- }
- case NodeTypeAnyFrameType: {
- fprintf(ar->f, "anyframe");
- if (node->data.anyframe_type.payload_type != nullptr) {
- fprintf(ar->f, "->");
- render_node_grouped(ar, node->data.anyframe_type.payload_type);
- }
- break;
- }
- case NodeTypeErrorType:
- fprintf(ar->f, "anyerror");
- break;
- case NodeTypeAsmExpr:
- {
- AstNodeAsmExpr *asm_expr = &node->data.asm_expr;
- const char *volatile_str = (asm_expr->volatile_token != nullptr) ? " volatile" : "";
- fprintf(ar->f, "asm%s (", volatile_str);
- render_node_ungrouped(ar, asm_expr->asm_template);
- fprintf(ar->f, ")");
- print_indent(ar);
- fprintf(ar->f, ": ");
- for (size_t i = 0; i < asm_expr->output_list.length; i += 1) {
- AsmOutput *asm_output = asm_expr->output_list.at(i);
-
- if (i != 0) {
- fprintf(ar->f, ",\n");
- print_indent(ar);
- }
-
- fprintf(ar->f, "[%s] \"%s\" (",
- buf_ptr(asm_output->asm_symbolic_name),
- buf_ptr(asm_output->constraint));
- if (asm_output->return_type) {
- fprintf(ar->f, "-> ");
- render_node_grouped(ar, asm_output->return_type);
- } else {
- fprintf(ar->f, "%s", buf_ptr(asm_output->variable_name));
- }
- fprintf(ar->f, ")");
- }
- fprintf(ar->f, "\n");
- print_indent(ar);
- fprintf(ar->f, ": ");
- for (size_t i = 0; i < asm_expr->input_list.length; i += 1) {
- AsmInput *asm_input = asm_expr->input_list.at(i);
-
- if (i != 0) {
- fprintf(ar->f, ",\n");
- print_indent(ar);
- }
-
- fprintf(ar->f, "[%s] \"%s\" (",
- buf_ptr(asm_input->asm_symbolic_name),
- buf_ptr(asm_input->constraint));
- render_node_grouped(ar, asm_input->expr);
- fprintf(ar->f, ")");
- }
- fprintf(ar->f, "\n");
- print_indent(ar);
- fprintf(ar->f, ": ");
- for (size_t i = 0; i < asm_expr->clobber_list.length; i += 1) {
- Buf *reg_name = asm_expr->clobber_list.at(i);
- if (i != 0) fprintf(ar->f, ", ");
- fprintf(ar->f, "\"%s\"", buf_ptr(reg_name));
- }
- fprintf(ar->f, ")");
- break;
- }
- case NodeTypeWhileExpr:
- {
- if (node->data.while_expr.name != nullptr) {
- fprintf(ar->f, "%s: ", buf_ptr(node->data.while_expr.name));
- }
- const char *inline_str = node->data.while_expr.is_inline ? "inline " : "";
- fprintf(ar->f, "%swhile (", inline_str);
- render_node_grouped(ar, node->data.while_expr.condition);
- fprintf(ar->f, ") ");
- if (node->data.while_expr.var_symbol) {
- fprintf(ar->f, "|%s| ", buf_ptr(node->data.while_expr.var_symbol));
- }
- if (node->data.while_expr.continue_expr) {
- fprintf(ar->f, ": (");
- render_node_grouped(ar, node->data.while_expr.continue_expr);
- fprintf(ar->f, ") ");
- }
- render_node_grouped(ar, node->data.while_expr.body);
- if (node->data.while_expr.else_node) {
- fprintf(ar->f, " else ");
- if (node->data.while_expr.err_symbol) {
- fprintf(ar->f, "|%s| ", buf_ptr(node->data.while_expr.err_symbol));
- }
- render_node_grouped(ar, node->data.while_expr.else_node);
- }
- break;
- }
- case NodeTypeBoolLiteral:
- {
- const char *bool_str = node->data.bool_literal.value ? "true" : "false";
- fprintf(ar->f, "%s", bool_str);
- break;
- }
- case NodeTypeIfBoolExpr:
- {
- fprintf(ar->f, "if (");
- render_node_grouped(ar, node->data.if_bool_expr.condition);
- fprintf(ar->f, ") ");
- render_node_grouped(ar, node->data.if_bool_expr.then_block);
- if (node->data.if_bool_expr.else_node) {
- fprintf(ar->f, " else ");
- render_node_grouped(ar, node->data.if_bool_expr.else_node);
- }
- break;
- }
- case NodeTypeNullLiteral:
- {
- fprintf(ar->f, "null");
- break;
- }
- case NodeTypeIfErrorExpr:
- {
- fprintf(ar->f, "if (");
- render_node_grouped(ar, node->data.if_err_expr.target_node);
- fprintf(ar->f, ") ");
- if (node->data.if_err_expr.var_symbol) {
- const char *ptr_str = node->data.if_err_expr.var_is_ptr ? "*" : "";
- const char *var_name = buf_ptr(node->data.if_err_expr.var_symbol);
- fprintf(ar->f, "|%s%s| ", ptr_str, var_name);
- }
- render_node_grouped(ar, node->data.if_err_expr.then_node);
- if (node->data.if_err_expr.else_node) {
- fprintf(ar->f, " else ");
- if (node->data.if_err_expr.err_symbol) {
- fprintf(ar->f, "|%s| ", buf_ptr(node->data.if_err_expr.err_symbol));
- }
- render_node_grouped(ar, node->data.if_err_expr.else_node);
- }
- break;
- }
- case NodeTypeIfOptional:
- {
- fprintf(ar->f, "if (");
- render_node_grouped(ar, node->data.test_expr.target_node);
- fprintf(ar->f, ") ");
- if (node->data.test_expr.var_symbol) {
- const char *ptr_str = node->data.test_expr.var_is_ptr ? "*" : "";
- const char *var_name = buf_ptr(node->data.test_expr.var_symbol);
- fprintf(ar->f, "|%s%s| ", ptr_str, var_name);
- }
- render_node_grouped(ar, node->data.test_expr.then_node);
- if (node->data.test_expr.else_node) {
- fprintf(ar->f, " else ");
- render_node_grouped(ar, node->data.test_expr.else_node);
- }
- break;
- }
- case NodeTypeSwitchExpr:
- {
- AstNodeSwitchExpr *switch_expr = &node->data.switch_expr;
- fprintf(ar->f, "switch (");
- render_node_grouped(ar, switch_expr->expr);
- fprintf(ar->f, ") {\n");
- ar->indent += ar->indent_size;
-
- for (size_t prong_i = 0; prong_i < switch_expr->prongs.length; prong_i += 1) {
- AstNode *prong_node = switch_expr->prongs.at(prong_i);
- AstNodeSwitchProng *switch_prong = &prong_node->data.switch_prong;
- print_indent(ar);
- for (size_t item_i = 0; item_i < switch_prong->items.length; item_i += 1) {
- AstNode *item_node = switch_prong->items.at(item_i);
- if (item_i != 0)
- fprintf(ar->f, ", ");
- if (item_node->type == NodeTypeSwitchRange) {
- AstNode *start_node = item_node->data.switch_range.start;
- AstNode *end_node = item_node->data.switch_range.end;
- render_node_grouped(ar, start_node);
- fprintf(ar->f, "...");
- render_node_grouped(ar, end_node);
- } else {
- render_node_grouped(ar, item_node);
- }
- }
- const char *else_str = (switch_prong->items.length == 0) ? "else" : "";
- fprintf(ar->f, "%s => ", else_str);
- if (switch_prong->var_symbol) {
- const char *star_str = switch_prong->var_is_ptr ? "*" : "";
- Buf *var_name = switch_prong->var_symbol->data.symbol_expr.symbol;
- fprintf(ar->f, "|%s%s| ", star_str, buf_ptr(var_name));
- }
- render_node_grouped(ar, switch_prong->expr);
- fprintf(ar->f, ",\n");
- }
-
- ar->indent -= ar->indent_size;
- print_indent(ar);
- fprintf(ar->f, "}");
- break;
- }
- case NodeTypeCompTime:
- {
- fprintf(ar->f, "comptime ");
- render_node_grouped(ar, node->data.comptime_expr.expr);
- break;
- }
- case NodeTypeNoSuspend:
- {
- fprintf(ar->f, "nosuspend ");
- render_node_grouped(ar, node->data.nosuspend_expr.expr);
- break;
- }
- case NodeTypeForExpr:
- {
- if (node->data.for_expr.name != nullptr) {
- fprintf(ar->f, "%s: ", buf_ptr(node->data.for_expr.name));
- }
- const char *inline_str = node->data.for_expr.is_inline ? "inline " : "";
- fprintf(ar->f, "%sfor (", inline_str);
- render_node_grouped(ar, node->data.for_expr.array_expr);
- fprintf(ar->f, ") ");
- if (node->data.for_expr.elem_node) {
- fprintf(ar->f, "|");
- if (node->data.for_expr.elem_is_ptr)
- fprintf(ar->f, "*");
- render_node_grouped(ar, node->data.for_expr.elem_node);
- if (node->data.for_expr.index_node) {
- fprintf(ar->f, ", ");
- render_node_grouped(ar, node->data.for_expr.index_node);
- }
- fprintf(ar->f, "| ");
- }
- render_node_grouped(ar, node->data.for_expr.body);
- if (node->data.for_expr.else_node) {
- fprintf(ar->f, " else");
- render_node_grouped(ar, node->data.for_expr.else_node);
- }
- break;
- }
- case NodeTypeContinue:
- {
- fprintf(ar->f, "continue");
- if (node->data.continue_expr.name != nullptr) {
- fprintf(ar->f, " :%s", buf_ptr(node->data.continue_expr.name));
- }
- break;
- }
- case NodeTypeUnreachable:
- {
- fprintf(ar->f, "unreachable");
- break;
- }
- case NodeTypeSliceExpr:
- {
- render_node_ungrouped(ar, node->data.slice_expr.array_ref_expr);
- fprintf(ar->f, "[");
- render_node_grouped(ar, node->data.slice_expr.start);
- fprintf(ar->f, "..");
- if (node->data.slice_expr.end)
- render_node_grouped(ar, node->data.slice_expr.end);
- fprintf(ar->f, "]");
- break;
- }
- case NodeTypeCatchExpr:
- {
- render_node_ungrouped(ar, node->data.unwrap_err_expr.op1);
- fprintf(ar->f, " catch ");
- if (node->data.unwrap_err_expr.symbol) {
- Buf *var_name = node->data.unwrap_err_expr.symbol->data.symbol_expr.symbol;
- fprintf(ar->f, "|%s| ", buf_ptr(var_name));
- }
- render_node_ungrouped(ar, node->data.unwrap_err_expr.op2);
- break;
- }
- case NodeTypeErrorSetDecl:
- {
- fprintf(ar->f, "error {\n");
- ar->indent += ar->indent_size;
-
- for (size_t i = 0; i < node->data.err_set_decl.decls.length; i += 1) {
- AstNode *field_node = node->data.err_set_decl.decls.at(i);
- switch (field_node->type) {
- case NodeTypeSymbol:
- print_indent(ar);
- print_symbol(ar, field_node->data.symbol_expr.symbol);
- fprintf(ar->f, ",\n");
- break;
- case NodeTypeErrorSetField:
- print_indent(ar);
- print_symbol(ar, field_node->data.err_set_field.field_name->data.symbol_expr.symbol);
- fprintf(ar->f, ",\n");
- break;
- default:
- zig_unreachable();
- }
- }
-
- ar->indent -= ar->indent_size;
- print_indent(ar);
- fprintf(ar->f, "}");
- break;
- }
- case NodeTypeResume:
- {
- fprintf(ar->f, "resume ");
- render_node_grouped(ar, node->data.resume_expr.expr);
- break;
- }
- case NodeTypeAwaitExpr:
- {
- fprintf(ar->f, "await ");
- render_node_grouped(ar, node->data.await_expr.expr);
- break;
- }
- case NodeTypeSuspend:
- {
- if (node->data.suspend.block != nullptr) {
- fprintf(ar->f, "suspend ");
- render_node_grouped(ar, node->data.suspend.block);
- } else {
- fprintf(ar->f, "suspend\n");
- }
- break;
- }
- case NodeTypeEnumLiteral:
- {
- fprintf(ar->f, ".%s", buf_ptr(&node->data.enum_literal.identifier->data.str_lit.str));
- break;
- }
- case NodeTypeAnyTypeField: {
- fprintf(ar->f, "anytype");
- break;
- }
- case NodeTypeParamDecl:
- case NodeTypeTestDecl:
- case NodeTypeStructField:
- case NodeTypeUsingNamespace:
- case NodeTypeErrorSetField:
- zig_panic("TODO more ast rendering");
- }
-}
-
-
-void ast_render(FILE *f, AstNode *node, int indent_size) {
- AstRender ar = {0};
- ar.f = f;
- ar.indent_size = indent_size;
- ar.indent = 0;
-
- render_node_grouped(&ar, node);
-}
-
-void AstNode::src() {
- fprintf(stderr, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize "\n",
- buf_ptr(this->owner->data.structure.root_struct->path),
- this->line + 1, this->column + 1);
-}
diff --git a/src/ast_render.hpp b/src/ast_render.hpp
deleted file mode 100644
index cf70b04694403b2d508df7d22a8f8fe0f1eb7752..0000000000000000000000000000000000000000
--- a/src/ast_render.hpp
+++ /dev/null
@@ -1,20 +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_AST_RENDER_HPP
-#define ZIG_AST_RENDER_HPP
-
-#include "all_types.hpp"
-#include "parser.hpp"
-
-#include
-
-void ast_print(FILE *f, AstNode *node, int indent);
-
-void ast_render(FILE *f, AstNode *node, int indent_size);
-
-#endif
diff --git a/src/astgen.zig b/src/astgen.zig
new file mode 100644
index 0000000000000000000000000000000000000000..2c091a86eccd3cc157cb6fcbb8c2dce3e7473fd0
--- /dev/null
+++ b/src/astgen.zig
@@ -0,0 +1,2396 @@
+const std = @import("std");
+const mem = std.mem;
+const Allocator = std.mem.Allocator;
+const Value = @import("value.zig").Value;
+const Type = @import("type.zig").Type;
+const TypedValue = @import("TypedValue.zig");
+const assert = std.debug.assert;
+const zir = @import("zir.zig");
+const Module = @import("Module.zig");
+const ast = std.zig.ast;
+const trace = @import("tracy.zig").trace;
+const Scope = Module.Scope;
+const InnerError = Module.InnerError;
+
+pub const ResultLoc = union(enum) {
+ /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the
+ /// expression should be generated.
+ discard,
+ /// The expression has an inferred type, and it will be evaluated as an rvalue.
+ none,
+ /// The expression must generate a pointer rather than a value. For example, the left hand side
+ /// of an assignment uses this kind of result location.
+ ref,
+ /// The expression will be type coerced into this type, but it will be evaluated as an rvalue.
+ ty: *zir.Inst,
+ /// The expression must store its result into this typed pointer.
+ ptr: *zir.Inst,
+ /// The expression must store its result into this allocation, which has an inferred type.
+ inferred_ptr: *zir.Inst.Tag.alloc_inferred.Type(),
+ /// The expression must store its result into this pointer, which is a typed pointer that
+ /// has been bitcasted to whatever the expression's type is.
+ bitcasted_ptr: *zir.Inst.UnOp,
+ /// There is a pointer for the expression to store its result into, however, its type
+ /// is inferred based on peer type resolution for a `zir.Inst.Block`.
+ block_ptr: *zir.Inst.Block,
+};
+
+pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst {
+ const type_src = scope.tree().token_locs[type_node.firstToken()].start;
+ const type_type = try addZIRInstConst(mod, scope, type_src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.type_type),
+ });
+ const type_rl: ResultLoc = .{ .ty = type_type };
+ return expr(mod, scope, type_rl, type_node);
+}
+
+fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
+ switch (node.tag) {
+ .Root => unreachable,
+ .Use => unreachable,
+ .TestDecl => unreachable,
+ .DocComment => unreachable,
+ .VarDecl => unreachable,
+ .SwitchCase => unreachable,
+ .SwitchElse => unreachable,
+ .Else => unreachable,
+ .Payload => unreachable,
+ .PointerPayload => unreachable,
+ .PointerIndexPayload => unreachable,
+ .ErrorTag => unreachable,
+ .FieldInitializer => unreachable,
+ .ContainerField => unreachable,
+
+ .Assign,
+ .AssignBitAnd,
+ .AssignBitOr,
+ .AssignBitShiftLeft,
+ .AssignBitShiftRight,
+ .AssignBitXor,
+ .AssignDiv,
+ .AssignSub,
+ .AssignSubWrap,
+ .AssignMod,
+ .AssignAdd,
+ .AssignAddWrap,
+ .AssignMul,
+ .AssignMulWrap,
+ .Add,
+ .AddWrap,
+ .Sub,
+ .SubWrap,
+ .Mul,
+ .MulWrap,
+ .Div,
+ .Mod,
+ .BitAnd,
+ .BitOr,
+ .BitShiftLeft,
+ .BitShiftRight,
+ .BitXor,
+ .BangEqual,
+ .EqualEqual,
+ .GreaterThan,
+ .GreaterOrEqual,
+ .LessThan,
+ .LessOrEqual,
+ .ArrayCat,
+ .ArrayMult,
+ .BoolAnd,
+ .BoolOr,
+ .Asm,
+ .StringLiteral,
+ .IntegerLiteral,
+ .Call,
+ .Unreachable,
+ .Return,
+ .If,
+ .While,
+ .BoolNot,
+ .AddressOf,
+ .FloatLiteral,
+ .UndefinedLiteral,
+ .BoolLiteral,
+ .NullLiteral,
+ .OptionalType,
+ .Block,
+ .LabeledBlock,
+ .Break,
+ .PtrType,
+ .GroupedExpression,
+ .ArrayType,
+ .ArrayTypeSentinel,
+ .EnumLiteral,
+ .MultilineStringLiteral,
+ .CharLiteral,
+ .Defer,
+ .Catch,
+ .ErrorUnion,
+ .MergeErrorSets,
+ .Range,
+ .OrElse,
+ .Await,
+ .BitNot,
+ .Negation,
+ .NegationWrap,
+ .Resume,
+ .Try,
+ .SliceType,
+ .Slice,
+ .ArrayInitializer,
+ .ArrayInitializerDot,
+ .StructInitializer,
+ .StructInitializerDot,
+ .Switch,
+ .For,
+ .Suspend,
+ .Continue,
+ .AnyType,
+ .ErrorType,
+ .FnProto,
+ .AnyFrameType,
+ .ErrorSetDecl,
+ .ContainerDecl,
+ .Comptime,
+ .Nosuspend,
+ => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}),
+
+ // @field can be assigned to
+ .BuiltinCall => {
+ const call = node.castTag(.BuiltinCall).?;
+ const tree = scope.tree();
+ const builtin_name = tree.tokenSlice(call.builtin_token);
+
+ if (!mem.eql(u8, builtin_name, "@field")) {
+ return mod.failNode(scope, node, "invalid left-hand side to assignment", .{});
+ }
+ },
+
+ // can be assigned to
+ .UnwrapOptional, .Deref, .Period, .ArrayAccess, .Identifier => {},
+ }
+ return expr(mod, scope, .ref, node);
+}
+
+/// Turn Zig AST into untyped ZIR istructions.
+pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {
+ switch (node.tag) {
+ .Root => unreachable, // Top-level declaration.
+ .Use => unreachable, // Top-level declaration.
+ .TestDecl => unreachable, // Top-level declaration.
+ .DocComment => unreachable, // Top-level declaration.
+ .VarDecl => unreachable, // Handled in `blockExpr`.
+ .SwitchCase => unreachable, // Handled in `switchExpr`.
+ .SwitchElse => unreachable, // Handled in `switchExpr`.
+ .Else => unreachable, // Handled explicitly the control flow expression functions.
+ .Payload => unreachable, // Handled explicitly.
+ .PointerPayload => unreachable, // Handled explicitly.
+ .PointerIndexPayload => unreachable, // Handled explicitly.
+ .ErrorTag => unreachable, // Handled explicitly.
+ .FieldInitializer => unreachable, // Handled explicitly.
+ .ContainerField => unreachable, // Handled explicitly.
+
+ .Assign => return rlWrapVoid(mod, scope, rl, node, try assign(mod, scope, node.castTag(.Assign).?)),
+ .AssignBitAnd => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitAnd).?, .bitand)),
+ .AssignBitOr => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitOr).?, .bitor)),
+ .AssignBitShiftLeft => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftLeft).?, .shl)),
+ .AssignBitShiftRight => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftRight).?, .shr)),
+ .AssignBitXor => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitXor).?, .xor)),
+ .AssignDiv => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignDiv).?, .div)),
+ .AssignSub => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSub).?, .sub)),
+ .AssignSubWrap => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSubWrap).?, .subwrap)),
+ .AssignMod => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMod).?, .mod_rem)),
+ .AssignAdd => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAdd).?, .add)),
+ .AssignAddWrap => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAddWrap).?, .addwrap)),
+ .AssignMul => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMul).?, .mul)),
+ .AssignMulWrap => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMulWrap).?, .mulwrap)),
+
+ .Add => return simpleBinOp(mod, scope, rl, node.castTag(.Add).?, .add),
+ .AddWrap => return simpleBinOp(mod, scope, rl, node.castTag(.AddWrap).?, .addwrap),
+ .Sub => return simpleBinOp(mod, scope, rl, node.castTag(.Sub).?, .sub),
+ .SubWrap => return simpleBinOp(mod, scope, rl, node.castTag(.SubWrap).?, .subwrap),
+ .Mul => return simpleBinOp(mod, scope, rl, node.castTag(.Mul).?, .mul),
+ .MulWrap => return simpleBinOp(mod, scope, rl, node.castTag(.MulWrap).?, .mulwrap),
+ .Div => return simpleBinOp(mod, scope, rl, node.castTag(.Div).?, .div),
+ .Mod => return simpleBinOp(mod, scope, rl, node.castTag(.Mod).?, .mod_rem),
+ .BitAnd => return simpleBinOp(mod, scope, rl, node.castTag(.BitAnd).?, .bitand),
+ .BitOr => return simpleBinOp(mod, scope, rl, node.castTag(.BitOr).?, .bitor),
+ .BitShiftLeft => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftLeft).?, .shl),
+ .BitShiftRight => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftRight).?, .shr),
+ .BitXor => return simpleBinOp(mod, scope, rl, node.castTag(.BitXor).?, .xor),
+
+ .BangEqual => return simpleBinOp(mod, scope, rl, node.castTag(.BangEqual).?, .cmp_neq),
+ .EqualEqual => return simpleBinOp(mod, scope, rl, node.castTag(.EqualEqual).?, .cmp_eq),
+ .GreaterThan => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterThan).?, .cmp_gt),
+ .GreaterOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterOrEqual).?, .cmp_gte),
+ .LessThan => return simpleBinOp(mod, scope, rl, node.castTag(.LessThan).?, .cmp_lt),
+ .LessOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.LessOrEqual).?, .cmp_lte),
+
+ .ArrayCat => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayCat).?, .array_cat),
+ .ArrayMult => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayMult).?, .array_mul),
+
+ .BoolAnd => return boolBinOp(mod, scope, rl, node.castTag(.BoolAnd).?),
+ .BoolOr => return boolBinOp(mod, scope, rl, node.castTag(.BoolOr).?),
+
+ .BoolNot => return rlWrap(mod, scope, rl, try boolNot(mod, scope, node.castTag(.BoolNot).?)),
+ .BitNot => return rlWrap(mod, scope, rl, try bitNot(mod, scope, node.castTag(.BitNot).?)),
+ .Negation => return rlWrap(mod, scope, rl, try negation(mod, scope, node.castTag(.Negation).?, .sub)),
+ .NegationWrap => return rlWrap(mod, scope, rl, try negation(mod, scope, node.castTag(.NegationWrap).?, .subwrap)),
+
+ .Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?),
+ .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)),
+ .StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)),
+ .IntegerLiteral => return rlWrap(mod, scope, rl, try integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?)),
+ .BuiltinCall => return builtinCall(mod, scope, rl, node.castTag(.BuiltinCall).?),
+ .Call => return callExpr(mod, scope, rl, node.castTag(.Call).?),
+ .Unreachable => return unreach(mod, scope, node.castTag(.Unreachable).?),
+ .Return => return ret(mod, scope, node.castTag(.Return).?),
+ .If => return ifExpr(mod, scope, rl, node.castTag(.If).?),
+ .While => return whileExpr(mod, scope, rl, node.castTag(.While).?),
+ .Period => return field(mod, scope, rl, node.castTag(.Period).?),
+ .Deref => return rlWrap(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)),
+ .AddressOf => return rlWrap(mod, scope, rl, try addressOf(mod, scope, node.castTag(.AddressOf).?)),
+ .FloatLiteral => return rlWrap(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)),
+ .UndefinedLiteral => return rlWrap(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)),
+ .BoolLiteral => return rlWrap(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)),
+ .NullLiteral => return rlWrap(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)),
+ .OptionalType => return rlWrap(mod, scope, rl, try optionalType(mod, scope, node.castTag(.OptionalType).?)),
+ .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?),
+ .Block => return rlWrapVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)),
+ .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?, .block),
+ .Break => return rlWrap(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)),
+ .PtrType => return rlWrap(mod, scope, rl, try ptrType(mod, scope, node.castTag(.PtrType).?)),
+ .GroupedExpression => return expr(mod, scope, rl, node.castTag(.GroupedExpression).?.expr),
+ .ArrayType => return rlWrap(mod, scope, rl, try arrayType(mod, scope, node.castTag(.ArrayType).?)),
+ .ArrayTypeSentinel => return rlWrap(mod, scope, rl, try arrayTypeSentinel(mod, scope, node.castTag(.ArrayTypeSentinel).?)),
+ .EnumLiteral => return rlWrap(mod, scope, rl, try enumLiteral(mod, scope, node.castTag(.EnumLiteral).?)),
+ .MultilineStringLiteral => return rlWrap(mod, scope, rl, try multilineStrLiteral(mod, scope, node.castTag(.MultilineStringLiteral).?)),
+ .CharLiteral => return rlWrap(mod, scope, rl, try charLiteral(mod, scope, node.castTag(.CharLiteral).?)),
+ .SliceType => return rlWrap(mod, scope, rl, try sliceType(mod, scope, node.castTag(.SliceType).?)),
+ .ErrorUnion => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.ErrorUnion).?, .error_union_type)),
+ .MergeErrorSets => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.MergeErrorSets).?, .merge_error_sets)),
+ .AnyFrameType => return rlWrap(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)),
+ .ErrorSetDecl => return errorSetDecl(mod, scope, rl, node.castTag(.ErrorSetDecl).?),
+ .ErrorType => return rlWrap(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)),
+ .For => return forExpr(mod, scope, rl, node.castTag(.For).?),
+ .ArrayAccess => return arrayAccess(mod, scope, rl, node.castTag(.ArrayAccess).?),
+ .Slice => return rlWrap(mod, scope, rl, try sliceExpr(mod, scope, node.castTag(.Slice).?)),
+ .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?),
+ .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?),
+ .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?),
+
+ .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),
+ .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}),
+ .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}),
+ .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}),
+ .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),
+ .ArrayInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializer", .{}),
+ .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}),
+ .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}),
+ .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}),
+ .Switch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Switch", .{}),
+ .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}),
+ .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}),
+ .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),
+ .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}),
+ .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}),
+ .Nosuspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Nosuspend", .{}),
+ }
+}
+
+fn comptimeKeyword(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Comptime) InnerError!*zir.Inst {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ return comptimeExpr(mod, scope, rl, node.expr);
+}
+
+pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {
+ const tree = parent_scope.tree();
+ const src = tree.token_locs[node.firstToken()].start;
+
+ // Optimization for labeled blocks: don't need to have 2 layers of blocks, we can reuse the existing one.
+ if (node.castTag(.LabeledBlock)) |block_node| {
+ return labeledBlockExpr(mod, parent_scope, rl, block_node, .block_comptime);
+ }
+
+ // Make a scope to collect generated instructions in the sub-expression.
+ var block_scope: Scope.GenZIR = .{
+ .parent = parent_scope,
+ .decl = parent_scope.decl().?,
+ .arena = parent_scope.arena(),
+ .instructions = .{},
+ };
+ defer block_scope.instructions.deinit(mod.gpa);
+
+ // No need to capture the result here because block_comptime_flat implies that the final
+ // instruction is the block's result value.
+ _ = try expr(mod, &block_scope.base, rl, node);
+
+ const block = try addZIRInstBlock(mod, parent_scope, src, .block_comptime_flat, .{
+ .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
+ });
+
+ return &block.base;
+}
+
+fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
+ const tree = parent_scope.tree();
+ const src = tree.token_locs[node.ltoken].start;
+
+ if (node.getLabel()) |break_label| {
+ // Look for the label in the scope.
+ var scope = parent_scope;
+ while (true) {
+ switch (scope.tag) {
+ .gen_zir => {
+ const gen_zir = scope.cast(Scope.GenZIR).?;
+ if (gen_zir.label) |label| {
+ if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
+ if (node.getRHS()) |rhs| {
+ // Most result location types can be forwarded directly; however
+ // if we need to write to a pointer which has an inferred type,
+ // proper type inference requires peer type resolution on the block's
+ // break operand expressions.
+ const branch_rl: ResultLoc = switch (label.result_loc) {
+ .discard, .none, .ty, .ptr, .ref => label.result_loc,
+ .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = label.block_inst },
+ };
+ const operand = try expr(mod, parent_scope, branch_rl, rhs);
+ return try addZIRInst(mod, scope, src, zir.Inst.Break, .{
+ .block = label.block_inst,
+ .operand = operand,
+ }, .{});
+ } else {
+ return try addZIRInst(mod, scope, src, zir.Inst.BreakVoid, .{
+ .block = label.block_inst,
+ }, .{});
+ }
+ }
+ }
+ scope = gen_zir.parent;
+ },
+ .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
+ .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
+ else => {
+ const label_name = try identifierTokenString(mod, parent_scope, break_label);
+ return mod.failTok(parent_scope, break_label, "label not found: '{}'", .{label_name});
+ },
+ }
+ }
+ } else {
+ return mod.failNode(parent_scope, &node.base, "TODO implement break from loop", .{});
+ }
+}
+
+pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block) InnerError!void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ try blockExprStmts(mod, parent_scope, &block_node.base, block_node.statements());
+}
+
+fn labeledBlockExpr(
+ mod: *Module,
+ parent_scope: *Scope,
+ rl: ResultLoc,
+ block_node: *ast.Node.LabeledBlock,
+ zir_tag: zir.Inst.Tag,
+) InnerError!*zir.Inst {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ assert(zir_tag == .block or zir_tag == .block_comptime);
+
+ const tree = parent_scope.tree();
+ const src = tree.token_locs[block_node.lbrace].start;
+
+ // Create the Block ZIR instruction so that we can put it into the GenZIR struct
+ // so that break statements can reference it.
+ const gen_zir = parent_scope.getGenZIR();
+ const block_inst = try gen_zir.arena.create(zir.Inst.Block);
+ block_inst.* = .{
+ .base = .{
+ .tag = zir_tag,
+ .src = src,
+ },
+ .positionals = .{
+ .body = .{ .instructions = undefined },
+ },
+ .kw_args = .{},
+ };
+
+ var block_scope: Scope.GenZIR = .{
+ .parent = parent_scope,
+ .decl = parent_scope.decl().?,
+ .arena = gen_zir.arena,
+ .instructions = .{},
+ // TODO @as here is working around a stage1 miscompilation bug :(
+ .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
+ .token = block_node.label,
+ .block_inst = block_inst,
+ .result_loc = rl,
+ }),
+ };
+ defer block_scope.instructions.deinit(mod.gpa);
+
+ try blockExprStmts(mod, &block_scope.base, &block_node.base, block_node.statements());
+
+ block_inst.positionals.body.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items);
+ try gen_zir.instructions.append(mod.gpa, &block_inst.base);
+
+ return &block_inst.base;
+}
+
+fn blockExprStmts(mod: *Module, parent_scope: *Scope, node: *ast.Node, statements: []*ast.Node) !void {
+ const tree = parent_scope.tree();
+
+ var block_arena = std.heap.ArenaAllocator.init(mod.gpa);
+ defer block_arena.deinit();
+
+ var scope = parent_scope;
+ for (statements) |statement| {
+ const src = tree.token_locs[statement.firstToken()].start;
+ _ = try addZIRNoOp(mod, scope, src, .dbg_stmt);
+ switch (statement.tag) {
+ .VarDecl => {
+ const var_decl_node = statement.castTag(.VarDecl).?;
+ scope = try varDecl(mod, scope, var_decl_node, &block_arena.allocator);
+ },
+ .Assign => try assign(mod, scope, statement.castTag(.Assign).?),
+ .AssignBitAnd => try assignOp(mod, scope, statement.castTag(.AssignBitAnd).?, .bitand),
+ .AssignBitOr => try assignOp(mod, scope, statement.castTag(.AssignBitOr).?, .bitor),
+ .AssignBitShiftLeft => try assignOp(mod, scope, statement.castTag(.AssignBitShiftLeft).?, .shl),
+ .AssignBitShiftRight => try assignOp(mod, scope, statement.castTag(.AssignBitShiftRight).?, .shr),
+ .AssignBitXor => try assignOp(mod, scope, statement.castTag(.AssignBitXor).?, .xor),
+ .AssignDiv => try assignOp(mod, scope, statement.castTag(.AssignDiv).?, .div),
+ .AssignSub => try assignOp(mod, scope, statement.castTag(.AssignSub).?, .sub),
+ .AssignSubWrap => try assignOp(mod, scope, statement.castTag(.AssignSubWrap).?, .subwrap),
+ .AssignMod => try assignOp(mod, scope, statement.castTag(.AssignMod).?, .mod_rem),
+ .AssignAdd => try assignOp(mod, scope, statement.castTag(.AssignAdd).?, .add),
+ .AssignAddWrap => try assignOp(mod, scope, statement.castTag(.AssignAddWrap).?, .addwrap),
+ .AssignMul => try assignOp(mod, scope, statement.castTag(.AssignMul).?, .mul),
+ .AssignMulWrap => try assignOp(mod, scope, statement.castTag(.AssignMulWrap).?, .mulwrap),
+
+ else => {
+ const possibly_unused_result = try expr(mod, scope, .none, statement);
+ if (!possibly_unused_result.tag.isNoReturn()) {
+ _ = try addZIRUnOp(mod, scope, src, .ensure_result_used, possibly_unused_result);
+ }
+ },
+ }
+ }
+}
+
+fn varDecl(
+ mod: *Module,
+ scope: *Scope,
+ node: *ast.Node.VarDecl,
+ block_arena: *Allocator,
+) InnerError!*Scope {
+ // TODO implement detection of shadowing
+ if (node.getComptimeToken()) |comptime_token| {
+ return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{});
+ }
+ if (node.getAlignNode()) |align_node| {
+ return mod.failNode(scope, align_node, "TODO implement alignment on locals", .{});
+ }
+ const tree = scope.tree();
+ const name_src = tree.token_locs[node.name_token].start;
+ const ident_name = try identifierTokenString(mod, scope, node.name_token);
+ const init_node = node.getInitNode() orelse
+ return mod.fail(scope, name_src, "variables must be initialized", .{});
+
+ switch (tree.token_ids[node.mut_token]) {
+ .Keyword_const => {
+ // Depending on the type of AST the initialization expression is, we may need an lvalue
+ // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
+ // the variable, no memory location needed.
+ const result_loc = if (nodeMayNeedMemoryLocation(init_node)) r: {
+ if (node.getTypeNode()) |type_node| {
+ const type_inst = try typeExpr(mod, scope, type_node);
+ const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);
+ break :r ResultLoc{ .ptr = alloc };
+ } else {
+ const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred);
+ break :r ResultLoc{ .inferred_ptr = alloc };
+ }
+ } else r: {
+ if (node.getTypeNode()) |type_node|
+ break :r ResultLoc{ .ty = try typeExpr(mod, scope, type_node) }
+ else
+ break :r .none;
+ };
+ const init_inst = try expr(mod, scope, result_loc, init_node);
+ const sub_scope = try block_arena.create(Scope.LocalVal);
+ sub_scope.* = .{
+ .parent = scope,
+ .gen_zir = scope.getGenZIR(),
+ .name = ident_name,
+ .inst = init_inst,
+ };
+ return &sub_scope.base;
+ },
+ .Keyword_var => {
+ const var_data: struct { result_loc: ResultLoc, alloc: *zir.Inst } = if (node.getTypeNode()) |type_node| a: {
+ const type_inst = try typeExpr(mod, scope, type_node);
+ const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);
+ break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
+ } else a: {
+ const alloc = try addZIRNoOp(mod, scope, name_src, .alloc_inferred);
+ break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred).? } };
+ };
+ const init_inst = try expr(mod, scope, var_data.result_loc, init_node);
+ const sub_scope = try block_arena.create(Scope.LocalPtr);
+ sub_scope.* = .{
+ .parent = scope,
+ .gen_zir = scope.getGenZIR(),
+ .name = ident_name,
+ .ptr = var_data.alloc,
+ };
+ return &sub_scope.base;
+ },
+ else => unreachable,
+ }
+}
+
+fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!void {
+ if (infix_node.lhs.castTag(.Identifier)) |ident| {
+ // This intentionally does not support @"_" syntax.
+ const ident_name = scope.tree().tokenSlice(ident.token);
+ if (mem.eql(u8, ident_name, "_")) {
+ _ = try expr(mod, scope, .discard, infix_node.rhs);
+ return;
+ }
+ }
+ const lvalue = try lvalExpr(mod, scope, infix_node.lhs);
+ _ = try expr(mod, scope, .{ .ptr = lvalue }, infix_node.rhs);
+}
+
+fn assignOp(
+ mod: *Module,
+ scope: *Scope,
+ infix_node: *ast.Node.SimpleInfixOp,
+ op_inst_tag: zir.Inst.Tag,
+) InnerError!void {
+ const lhs_ptr = try lvalExpr(mod, scope, infix_node.lhs);
+ const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr);
+ const lhs_type = try addZIRUnOp(mod, scope, lhs_ptr.src, .typeof, lhs);
+ const rhs = try expr(mod, scope, .{ .ty = lhs_type }, infix_node.rhs);
+
+ const tree = scope.tree();
+ const src = tree.token_locs[infix_node.op_token].start;
+
+ const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
+ _ = try addZIRBinOp(mod, scope, src, .store, lhs_ptr, result);
+}
+
+fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+ const bool_type = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.bool_type),
+ });
+ const operand = try expr(mod, scope, .{ .ty = bool_type }, node.rhs);
+ return addZIRUnOp(mod, scope, src, .boolnot, operand);
+}
+
+fn bitNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+ const operand = try expr(mod, scope, .none, node.rhs);
+ return addZIRUnOp(mod, scope, src, .bitnot, operand);
+}
+
+fn negation(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp, op_inst_tag: zir.Inst.Tag) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+
+ const lhs = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.comptime_int),
+ .val = Value.initTag(.zero),
+ });
+ const rhs = try expr(mod, scope, .none, node.rhs);
+
+ return addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
+}
+
+fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
+ return expr(mod, scope, .ref, node.rhs);
+}
+
+fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+ const operand = try typeExpr(mod, scope, node.rhs);
+ return addZIRUnOp(mod, scope, src, .optional_type, operand);
+}
+
+fn sliceType(mod: *Module, scope: *Scope, node: *ast.Node.SliceType) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+ return ptrSliceType(mod, scope, src, &node.ptr_info, node.rhs, .Slice);
+}
+
+fn ptrType(mod: *Module, scope: *Scope, node: *ast.Node.PtrType) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+ return ptrSliceType(mod, scope, src, &node.ptr_info, node.rhs, switch (tree.token_ids[node.op_token]) {
+ .Asterisk, .AsteriskAsterisk => .One,
+ // TODO stage1 type inference bug
+ .LBracket => @as(std.builtin.TypeInfo.Pointer.Size, switch (tree.token_ids[node.op_token + 2]) {
+ .Identifier => .C,
+ else => .Many,
+ }),
+ else => unreachable,
+ });
+}
+
+fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, rhs: *ast.Node, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*zir.Inst {
+ const simple = ptr_info.allowzero_token == null and
+ ptr_info.align_info == null and
+ ptr_info.volatile_token == null and
+ ptr_info.sentinel == null;
+
+ if (simple) {
+ const child_type = try typeExpr(mod, scope, rhs);
+ const mutable = ptr_info.const_token == null;
+ // TODO stage1 type inference bug
+ const T = zir.Inst.Tag;
+ return addZIRUnOp(mod, scope, src, switch (size) {
+ .One => if (mutable) T.single_mut_ptr_type else T.single_const_ptr_type,
+ .Many => if (mutable) T.many_mut_ptr_type else T.many_const_ptr_type,
+ .C => if (mutable) T.c_mut_ptr_type else T.c_const_ptr_type,
+ .Slice => if (mutable) T.mut_slice_type else T.const_slice_type,
+ }, child_type);
+ }
+
+ var kw_args: std.meta.fieldInfo(zir.Inst.PtrType, "kw_args").field_type = .{};
+ kw_args.size = size;
+ kw_args.@"allowzero" = ptr_info.allowzero_token != null;
+ if (ptr_info.align_info) |some| {
+ kw_args.@"align" = try expr(mod, scope, .none, some.node);
+ if (some.bit_range) |bit_range| {
+ kw_args.align_bit_start = try expr(mod, scope, .none, bit_range.start);
+ kw_args.align_bit_end = try expr(mod, scope, .none, bit_range.end);
+ }
+ }
+ kw_args.mutable = ptr_info.const_token == null;
+ kw_args.@"volatile" = ptr_info.volatile_token != null;
+ if (ptr_info.sentinel) |some| {
+ kw_args.sentinel = try expr(mod, scope, .none, some);
+ }
+
+ const child_type = try typeExpr(mod, scope, rhs);
+ if (kw_args.sentinel) |some| {
+ kw_args.sentinel = try addZIRBinOp(mod, scope, some.src, .as, child_type, some);
+ }
+
+ return addZIRInst(mod, scope, src, zir.Inst.PtrType, .{ .child_type = child_type }, kw_args);
+}
+
+fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+ const usize_type = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.usize_type),
+ });
+
+ // TODO check for [_]T
+ const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr);
+ const elem_type = try typeExpr(mod, scope, node.rhs);
+
+ return addZIRBinOp(mod, scope, src, .array_type, len, elem_type);
+}
+
+fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSentinel) !*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+ const usize_type = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.usize_type),
+ });
+
+ // TODO check for [_]T
+ const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr);
+ const sentinel_uncasted = try expr(mod, scope, .none, node.sentinel);
+ const elem_type = try typeExpr(mod, scope, node.rhs);
+ const sentinel = try addZIRBinOp(mod, scope, src, .as, elem_type, sentinel_uncasted);
+
+ return addZIRInst(mod, scope, src, zir.Inst.ArrayTypeSentinel, .{
+ .len = len,
+ .sentinel = sentinel,
+ .elem_type = elem_type,
+ }, .{});
+}
+
+fn anyFrameType(mod: *Module, scope: *Scope, node: *ast.Node.AnyFrameType) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.anyframe_token].start;
+ if (node.result) |some| {
+ const return_type = try typeExpr(mod, scope, some.return_type);
+ return addZIRUnOp(mod, scope, src, .anyframe_type, return_type);
+ } else {
+ return addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.anyframe_type),
+ });
+ }
+}
+
+fn typeInixOp(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp, op_inst_tag: zir.Inst.Tag) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+ const error_set = try typeExpr(mod, scope, node.lhs);
+ const payload = try typeExpr(mod, scope, node.rhs);
+ return addZIRBinOp(mod, scope, src, op_inst_tag, error_set, payload);
+}
+
+fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.name].start;
+ const name = try identifierTokenString(mod, scope, node.name);
+
+ return addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{});
+}
+
+fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.rtoken].start;
+
+ const operand = try expr(mod, scope, .ref, node.lhs);
+ return rlWrapPtr(mod, scope, rl, try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand));
+}
+
+fn errorSetDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ErrorSetDecl) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.error_token].start;
+ const decls = node.decls();
+ const fields = try scope.arena().alloc([]const u8, decls.len);
+
+ for (decls) |decl, i| {
+ const tag = decl.castTag(.ErrorTag).?;
+ fields[i] = try identifierTokenString(mod, scope, tag.name_token);
+ }
+
+ // analyzing the error set results in a decl ref, so we might need to dereference it
+ return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{}));
+}
+
+fn errorType(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.token].start;
+ return addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.anyerror_type),
+ });
+}
+
+fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Catch) InnerError!*zir.Inst {
+ return orelseCatchExpr(mod, scope, rl, node.lhs, node.op_token, .iserr, .unwrap_err_unsafe, node.rhs, node.payload);
+}
+
+fn orelseExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
+ return orelseCatchExpr(mod, scope, rl, node.lhs, node.op_token, .isnull, .unwrap_optional_unsafe, node.rhs, null);
+}
+
+fn orelseCatchExpr(
+ mod: *Module,
+ scope: *Scope,
+ rl: ResultLoc,
+ lhs: *ast.Node,
+ op_token: ast.TokenIndex,
+ cond_op: zir.Inst.Tag,
+ unwrap_op: zir.Inst.Tag,
+ rhs: *ast.Node,
+ payload_node: ?*ast.Node,
+) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[op_token].start;
+
+ const operand_ptr = try expr(mod, scope, .ref, lhs);
+ // TODO we could avoid an unnecessary copy if .iserr, .isnull took a pointer
+ const err_union = try addZIRUnOp(mod, scope, src, .deref, operand_ptr);
+ const cond = try addZIRUnOp(mod, scope, src, cond_op, err_union);
+
+ var block_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = scope.decl().?,
+ .arena = scope.arena(),
+ .instructions = .{},
+ };
+ defer block_scope.instructions.deinit(mod.gpa);
+
+ const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
+ .condition = cond,
+ .then_body = undefined, // populated below
+ .else_body = undefined, // populated below
+ }, .{});
+
+ const block = try addZIRInstBlock(mod, scope, src, .block, .{
+ .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
+ });
+
+ // Most result location types can be forwarded directly; however
+ // if we need to write to a pointer which has an inferred type,
+ // proper type inference requires peer type resolution on the if's
+ // branches.
+ const branch_rl: ResultLoc = switch (rl) {
+ .discard, .none, .ty, .ptr, .ref => rl,
+ .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
+ };
+
+ var then_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = block_scope.decl,
+ .arena = block_scope.arena,
+ .instructions = .{},
+ };
+ defer then_scope.instructions.deinit(mod.gpa);
+
+ var err_val_scope: Scope.LocalVal = undefined;
+ const then_sub_scope = blk: {
+ const payload = payload_node orelse
+ break :blk &then_scope.base;
+
+ const err_name = tree.tokenSlice(payload.castTag(.Payload).?.error_symbol.firstToken());
+ if (mem.eql(u8, err_name, "_"))
+ break :blk &then_scope.base;
+
+ const unwrapped_err_ptr = try addZIRUnOp(mod, &then_scope.base, src, .unwrap_err_code, operand_ptr);
+ err_val_scope = .{
+ .parent = &then_scope.base,
+ .gen_zir = &then_scope,
+ .name = err_name,
+ .inst = try addZIRUnOp(mod, &then_scope.base, src, .deref, unwrapped_err_ptr),
+ };
+ break :blk &err_val_scope.base;
+ };
+
+ _ = try addZIRInst(mod, &then_scope.base, src, zir.Inst.Break, .{
+ .block = block,
+ .operand = try expr(mod, then_sub_scope, branch_rl, rhs),
+ }, .{});
+
+ var else_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = block_scope.decl,
+ .arena = block_scope.arena,
+ .instructions = .{},
+ };
+ defer else_scope.instructions.deinit(mod.gpa);
+
+ const unwrapped_payload = try addZIRUnOp(mod, &else_scope.base, src, unwrap_op, operand_ptr);
+ _ = try addZIRInst(mod, &else_scope.base, src, zir.Inst.Break, .{
+ .block = block,
+ .operand = unwrapped_payload,
+ }, .{});
+
+ condbr.positionals.then_body = .{ .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items) };
+ condbr.positionals.else_body = .{ .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items) };
+ return rlWrapPtr(mod, scope, rl, &block.base);
+}
+
+/// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating.
+/// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used.
+fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {
+ const ident_name_1 = try identifierTokenString(mod, scope, token1);
+ const ident_name_2 = try identifierTokenString(mod, scope, token2);
+ return mem.eql(u8, ident_name_1, ident_name_2);
+}
+
+/// Identifier token -> String (allocated in scope.arena())
+fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
+ const tree = scope.tree();
+
+ const ident_name = tree.tokenSlice(token);
+ if (mem.startsWith(u8, ident_name, "@")) {
+ const raw_string = ident_name[1..];
+ var bad_index: usize = undefined;
+ return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) {
+ error.InvalidCharacter => {
+ const bad_byte = raw_string[bad_index];
+ const src = tree.token_locs[token].start;
+ return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
+ },
+ else => |e| return e,
+ };
+ }
+ return ident_name;
+}
+
+pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.token].start;
+
+ const ident_name = try identifierTokenString(mod, scope, node.token);
+
+ return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{});
+}
+
+fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+
+ const lhs = try expr(mod, scope, .ref, node.lhs);
+ const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?);
+
+ return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{}));
+}
+
+fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ArrayAccess) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.rtoken].start;
+
+ const array_ptr = try expr(mod, scope, .ref, node.lhs);
+ const index = try expr(mod, scope, .none, node.index_expr);
+
+ return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ElemPtr, .{ .array_ptr = array_ptr, .index = index }, .{}));
+}
+
+fn sliceExpr(mod: *Module, scope: *Scope, node: *ast.Node.Slice) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.rtoken].start;
+
+ const usize_type = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.usize_type),
+ });
+
+ const array_ptr = try expr(mod, scope, .ref, node.lhs);
+ const start = try expr(mod, scope, .{ .ty = usize_type }, node.start);
+
+ if (node.end == null and node.sentinel == null) {
+ return try addZIRBinOp(mod, scope, src, .slice_start, array_ptr, start);
+ }
+
+ const end = if (node.end) |end| try expr(mod, scope, .{ .ty = usize_type }, end) else null;
+ // we could get the child type here, but it is easier to just do it in semantic analysis.
+ const sentinel = if (node.sentinel) |sentinel| try expr(mod, scope, .none, sentinel) else null;
+
+ return try addZIRInst(
+ mod,
+ scope,
+ src,
+ zir.Inst.Slice,
+ .{ .array_ptr = array_ptr, .start = start },
+ .{ .end = end, .sentinel = sentinel },
+ );
+}
+
+fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.rtoken].start;
+ const lhs = try expr(mod, scope, .none, node.lhs);
+ return addZIRUnOp(mod, scope, src, .deref, lhs);
+}
+
+fn simpleBinOp(
+ mod: *Module,
+ scope: *Scope,
+ rl: ResultLoc,
+ infix_node: *ast.Node.SimpleInfixOp,
+ op_inst_tag: zir.Inst.Tag,
+) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[infix_node.op_token].start;
+
+ const lhs = try expr(mod, scope, .none, infix_node.lhs);
+ const rhs = try expr(mod, scope, .none, infix_node.rhs);
+
+ const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
+ return rlWrap(mod, scope, rl, result);
+}
+
+fn boolBinOp(
+ mod: *Module,
+ scope: *Scope,
+ rl: ResultLoc,
+ infix_node: *ast.Node.SimpleInfixOp,
+) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[infix_node.op_token].start;
+ const bool_type = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.bool_type),
+ });
+
+ var block_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = scope.decl().?,
+ .arena = scope.arena(),
+ .instructions = .{},
+ };
+ defer block_scope.instructions.deinit(mod.gpa);
+
+ const lhs = try expr(mod, scope, .{ .ty = bool_type }, infix_node.lhs);
+ const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
+ .condition = lhs,
+ .then_body = undefined, // populated below
+ .else_body = undefined, // populated below
+ }, .{});
+
+ const block = try addZIRInstBlock(mod, scope, src, .block, .{
+ .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
+ });
+
+ var rhs_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = block_scope.decl,
+ .arena = block_scope.arena,
+ .instructions = .{},
+ };
+ defer rhs_scope.instructions.deinit(mod.gpa);
+
+ const rhs = try expr(mod, &rhs_scope.base, .{ .ty = bool_type }, infix_node.rhs);
+ _ = try addZIRInst(mod, &rhs_scope.base, src, zir.Inst.Break, .{
+ .block = block,
+ .operand = rhs,
+ }, .{});
+
+ var const_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = block_scope.decl,
+ .arena = block_scope.arena,
+ .instructions = .{},
+ };
+ defer const_scope.instructions.deinit(mod.gpa);
+
+ const is_bool_and = infix_node.base.tag == .BoolAnd;
+ _ = try addZIRInst(mod, &const_scope.base, src, zir.Inst.Break, .{
+ .block = block,
+ .operand = try addZIRInstConst(mod, &const_scope.base, src, .{
+ .ty = Type.initTag(.bool),
+ .val = if (is_bool_and) Value.initTag(.bool_false) else Value.initTag(.bool_true),
+ }),
+ }, .{});
+
+ if (is_bool_and) {
+ // if lhs // AND
+ // break rhs
+ // else
+ // break false
+ condbr.positionals.then_body = .{ .instructions = try rhs_scope.arena.dupe(*zir.Inst, rhs_scope.instructions.items) };
+ condbr.positionals.else_body = .{ .instructions = try const_scope.arena.dupe(*zir.Inst, const_scope.instructions.items) };
+ } else {
+ // if lhs // OR
+ // break true
+ // else
+ // break rhs
+ condbr.positionals.then_body = .{ .instructions = try const_scope.arena.dupe(*zir.Inst, const_scope.instructions.items) };
+ condbr.positionals.else_body = .{ .instructions = try rhs_scope.arena.dupe(*zir.Inst, rhs_scope.instructions.items) };
+ }
+
+ return rlWrap(mod, scope, rl, &block.base);
+}
+
+const CondKind = union(enum) {
+ bool,
+ optional: ?*zir.Inst,
+ err_union: ?*zir.Inst,
+
+ fn cond(self: *CondKind, mod: *Module, block_scope: *Scope.GenZIR, src: usize, cond_node: *ast.Node) !*zir.Inst {
+ switch (self.*) {
+ .bool => {
+ const bool_type = try addZIRInstConst(mod, &block_scope.base, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.bool_type),
+ });
+ return try expr(mod, &block_scope.base, .{ .ty = bool_type }, cond_node);
+ },
+ .optional => {
+ const cond_ptr = try expr(mod, &block_scope.base, .ref, cond_node);
+ self.* = .{ .optional = cond_ptr };
+ const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, cond_ptr);
+ return try addZIRUnOp(mod, &block_scope.base, src, .isnonnull, result);
+ },
+ .err_union => {
+ const err_ptr = try expr(mod, &block_scope.base, .ref, cond_node);
+ self.* = .{ .err_union = err_ptr };
+ const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, err_ptr);
+ return try addZIRUnOp(mod, &block_scope.base, src, .iserr, result);
+ },
+ }
+ }
+
+ fn thenSubScope(self: CondKind, mod: *Module, then_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope {
+ if (self == .bool) return &then_scope.base;
+
+ const payload = payload_node.?.castTag(.PointerPayload) orelse {
+ // condition is error union and payload is not explicitly ignored
+ _ = try addZIRUnOp(mod, &then_scope.base, src, .ensure_err_payload_void, self.err_union.?);
+ return &then_scope.base;
+ };
+ const is_ptr = payload.ptr_token != null;
+ const ident_node = payload.value_symbol.castTag(.Identifier).?;
+
+ // This intentionally does not support @"_" syntax.
+ const ident_name = then_scope.base.tree().tokenSlice(ident_node.token);
+ if (mem.eql(u8, ident_name, "_")) {
+ if (is_ptr)
+ return mod.failTok(&then_scope.base, payload.ptr_token.?, "pointer modifier invalid on discard", .{});
+ return &then_scope.base;
+ }
+
+ return mod.failNode(&then_scope.base, payload.value_symbol, "TODO implement payload symbols", .{});
+ }
+
+ fn elseSubScope(self: CondKind, mod: *Module, else_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope {
+ if (self != .err_union) return &else_scope.base;
+
+ const payload_ptr = try addZIRUnOp(mod, &else_scope.base, src, .unwrap_err_unsafe, self.err_union.?);
+
+ const payload = payload_node.?.castTag(.Payload).?;
+ const ident_node = payload.error_symbol.castTag(.Identifier).?;
+
+ // This intentionally does not support @"_" syntax.
+ const ident_name = else_scope.base.tree().tokenSlice(ident_node.token);
+ if (mem.eql(u8, ident_name, "_")) {
+ return &else_scope.base;
+ }
+
+ return mod.failNode(&else_scope.base, payload.error_symbol, "TODO implement payload symbols", .{});
+ }
+};
+
+fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) InnerError!*zir.Inst {
+ var cond_kind: CondKind = .bool;
+ if (if_node.payload) |_| cond_kind = .{ .optional = null };
+ if (if_node.@"else") |else_node| {
+ if (else_node.payload) |payload| {
+ cond_kind = .{ .err_union = null };
+ }
+ }
+ var block_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = scope.decl().?,
+ .arena = scope.arena(),
+ .instructions = .{},
+ };
+ defer block_scope.instructions.deinit(mod.gpa);
+
+ const tree = scope.tree();
+ const if_src = tree.token_locs[if_node.if_token].start;
+ const cond = try cond_kind.cond(mod, &block_scope, if_src, if_node.condition);
+
+ const condbr = try addZIRInstSpecial(mod, &block_scope.base, if_src, zir.Inst.CondBr, .{
+ .condition = cond,
+ .then_body = undefined, // populated below
+ .else_body = undefined, // populated below
+ }, .{});
+
+ const block = try addZIRInstBlock(mod, scope, if_src, .block, .{
+ .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
+ });
+
+ const then_src = tree.token_locs[if_node.body.lastToken()].start;
+ var then_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = block_scope.decl,
+ .arena = block_scope.arena,
+ .instructions = .{},
+ };
+ defer then_scope.instructions.deinit(mod.gpa);
+
+ // declare payload to the then_scope
+ const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, if_node.payload);
+
+ // Most result location types can be forwarded directly; however
+ // if we need to write to a pointer which has an inferred type,
+ // proper type inference requires peer type resolution on the if's
+ // branches.
+ const branch_rl: ResultLoc = switch (rl) {
+ .discard, .none, .ty, .ptr, .ref => rl,
+ .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
+ };
+
+ const then_result = try expr(mod, then_sub_scope, branch_rl, if_node.body);
+ if (!then_result.tag.isNoReturn()) {
+ _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{
+ .block = block,
+ .operand = then_result,
+ }, .{});
+ }
+ condbr.positionals.then_body = .{
+ .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
+ };
+
+ var else_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = block_scope.decl,
+ .arena = block_scope.arena,
+ .instructions = .{},
+ };
+ defer else_scope.instructions.deinit(mod.gpa);
+
+ if (if_node.@"else") |else_node| {
+ const else_src = tree.token_locs[else_node.body.lastToken()].start;
+ // declare payload to the then_scope
+ const else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload);
+
+ const else_result = try expr(mod, else_sub_scope, branch_rl, else_node.body);
+ if (!else_result.tag.isNoReturn()) {
+ _ = try addZIRInst(mod, else_sub_scope, else_src, zir.Inst.Break, .{
+ .block = block,
+ .operand = else_result,
+ }, .{});
+ }
+ } else {
+ // TODO Optimization opportunity: we can avoid an allocation and a memcpy here
+ // by directly allocating the body for this one instruction.
+ const else_src = tree.token_locs[if_node.lastToken()].start;
+ _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.BreakVoid, .{
+ .block = block,
+ }, .{});
+ }
+ condbr.positionals.else_body = .{
+ .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
+ };
+
+ return &block.base;
+}
+
+fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.While) InnerError!*zir.Inst {
+ var cond_kind: CondKind = .bool;
+ if (while_node.payload) |_| cond_kind = .{ .optional = null };
+ if (while_node.@"else") |else_node| {
+ if (else_node.payload) |payload| {
+ cond_kind = .{ .err_union = null };
+ }
+ }
+
+ if (while_node.label) |tok|
+ return mod.failTok(scope, tok, "TODO labeled while", .{});
+
+ if (while_node.inline_token) |tok|
+ return mod.failTok(scope, tok, "TODO inline while", .{});
+
+ var expr_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = scope.decl().?,
+ .arena = scope.arena(),
+ .instructions = .{},
+ };
+ defer expr_scope.instructions.deinit(mod.gpa);
+
+ var loop_scope: Scope.GenZIR = .{
+ .parent = &expr_scope.base,
+ .decl = expr_scope.decl,
+ .arena = expr_scope.arena,
+ .instructions = .{},
+ };
+ defer loop_scope.instructions.deinit(mod.gpa);
+
+ var continue_scope: Scope.GenZIR = .{
+ .parent = &loop_scope.base,
+ .decl = loop_scope.decl,
+ .arena = loop_scope.arena,
+ .instructions = .{},
+ };
+ defer continue_scope.instructions.deinit(mod.gpa);
+
+ const tree = scope.tree();
+ const while_src = tree.token_locs[while_node.while_token].start;
+ const void_type = try addZIRInstConst(mod, scope, while_src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.void_type),
+ });
+ const cond = try cond_kind.cond(mod, &continue_scope, while_src, while_node.condition);
+
+ const condbr = try addZIRInstSpecial(mod, &continue_scope.base, while_src, zir.Inst.CondBr, .{
+ .condition = cond,
+ .then_body = undefined, // populated below
+ .else_body = undefined, // populated below
+ }, .{});
+ const cond_block = try addZIRInstBlock(mod, &loop_scope.base, while_src, .block, .{
+ .instructions = try loop_scope.arena.dupe(*zir.Inst, continue_scope.instructions.items),
+ });
+ // TODO avoid emitting the continue expr when there
+ // are no jumps to it. This happens when the last statement of a while body is noreturn
+ // and there are no `continue` statements.
+ // The "repeat" at the end of a loop body is implied.
+ if (while_node.continue_expr) |cont_expr| {
+ _ = try expr(mod, &loop_scope.base, .{ .ty = void_type }, cont_expr);
+ }
+ const loop = try addZIRInstLoop(mod, &expr_scope.base, while_src, .{
+ .instructions = try expr_scope.arena.dupe(*zir.Inst, loop_scope.instructions.items),
+ });
+ const while_block = try addZIRInstBlock(mod, scope, while_src, .block, .{
+ .instructions = try expr_scope.arena.dupe(*zir.Inst, expr_scope.instructions.items),
+ });
+
+ const then_src = tree.token_locs[while_node.body.lastToken()].start;
+ var then_scope: Scope.GenZIR = .{
+ .parent = &continue_scope.base,
+ .decl = continue_scope.decl,
+ .arena = continue_scope.arena,
+ .instructions = .{},
+ };
+ defer then_scope.instructions.deinit(mod.gpa);
+
+ // declare payload to the then_scope
+ const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, while_node.payload);
+
+ // Most result location types can be forwarded directly; however
+ // if we need to write to a pointer which has an inferred type,
+ // proper type inference requires peer type resolution on the while's
+ // branches.
+ const branch_rl: ResultLoc = switch (rl) {
+ .discard, .none, .ty, .ptr, .ref => rl,
+ .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = while_block },
+ };
+
+ const then_result = try expr(mod, then_sub_scope, branch_rl, while_node.body);
+ if (!then_result.tag.isNoReturn()) {
+ _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{
+ .block = cond_block,
+ .operand = then_result,
+ }, .{});
+ }
+ condbr.positionals.then_body = .{
+ .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
+ };
+
+ var else_scope: Scope.GenZIR = .{
+ .parent = &continue_scope.base,
+ .decl = continue_scope.decl,
+ .arena = continue_scope.arena,
+ .instructions = .{},
+ };
+ defer else_scope.instructions.deinit(mod.gpa);
+
+ if (while_node.@"else") |else_node| {
+ const else_src = tree.token_locs[else_node.body.lastToken()].start;
+ // declare payload to the then_scope
+ const else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload);
+
+ const else_result = try expr(mod, else_sub_scope, branch_rl, else_node.body);
+ if (!else_result.tag.isNoReturn()) {
+ _ = try addZIRInst(mod, else_sub_scope, else_src, zir.Inst.Break, .{
+ .block = while_block,
+ .operand = else_result,
+ }, .{});
+ }
+ } else {
+ const else_src = tree.token_locs[while_node.lastToken()].start;
+ _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.BreakVoid, .{
+ .block = while_block,
+ }, .{});
+ }
+ condbr.positionals.else_body = .{
+ .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
+ };
+ return &while_block.base;
+}
+
+fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For) InnerError!*zir.Inst {
+ if (for_node.label) |tok|
+ return mod.failTok(scope, tok, "TODO labeled for", .{});
+
+ if (for_node.inline_token) |tok|
+ return mod.failTok(scope, tok, "TODO inline for", .{});
+
+ var for_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = scope.decl().?,
+ .arena = scope.arena(),
+ .instructions = .{},
+ };
+ defer for_scope.instructions.deinit(mod.gpa);
+
+ // setup variables and constants
+ const tree = scope.tree();
+ const for_src = tree.token_locs[for_node.for_token].start;
+ const index_ptr = blk: {
+ const usize_type = try addZIRInstConst(mod, &for_scope.base, for_src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.usize_type),
+ });
+ const index_ptr = try addZIRUnOp(mod, &for_scope.base, for_src, .alloc, usize_type);
+ // initialize to zero
+ const zero = try addZIRInstConst(mod, &for_scope.base, for_src, .{
+ .ty = Type.initTag(.usize),
+ .val = Value.initTag(.zero),
+ });
+ _ = try addZIRBinOp(mod, &for_scope.base, for_src, .store, index_ptr, zero);
+ break :blk index_ptr;
+ };
+ const array_ptr = try expr(mod, &for_scope.base, .ref, for_node.array_expr);
+ _ = try addZIRUnOp(mod, &for_scope.base, for_node.array_expr.firstToken(), .ensure_indexable, array_ptr);
+ const cond_src = tree.token_locs[for_node.array_expr.firstToken()].start;
+ const len_ptr = try addZIRInst(mod, &for_scope.base, cond_src, zir.Inst.FieldPtr, .{
+ .object_ptr = array_ptr,
+ .field_name = try addZIRInst(mod, &for_scope.base, cond_src, zir.Inst.Str, .{ .bytes = "len" }, .{}),
+ }, .{});
+
+ var loop_scope: Scope.GenZIR = .{
+ .parent = &for_scope.base,
+ .decl = for_scope.decl,
+ .arena = for_scope.arena,
+ .instructions = .{},
+ };
+ defer loop_scope.instructions.deinit(mod.gpa);
+
+ var cond_scope: Scope.GenZIR = .{
+ .parent = &loop_scope.base,
+ .decl = loop_scope.decl,
+ .arena = loop_scope.arena,
+ .instructions = .{},
+ };
+ defer cond_scope.instructions.deinit(mod.gpa);
+
+ // check condition i < array_expr.len
+ const index = try addZIRUnOp(mod, &cond_scope.base, cond_src, .deref, index_ptr);
+ const len = try addZIRUnOp(mod, &cond_scope.base, cond_src, .deref, len_ptr);
+ const cond = try addZIRBinOp(mod, &cond_scope.base, cond_src, .cmp_lt, index, len);
+
+ const condbr = try addZIRInstSpecial(mod, &cond_scope.base, for_src, zir.Inst.CondBr, .{
+ .condition = cond,
+ .then_body = undefined, // populated below
+ .else_body = undefined, // populated below
+ }, .{});
+ const cond_block = try addZIRInstBlock(mod, &loop_scope.base, for_src, .block, .{
+ .instructions = try loop_scope.arena.dupe(*zir.Inst, cond_scope.instructions.items),
+ });
+
+ // increment index variable
+ const one = try addZIRInstConst(mod, &loop_scope.base, for_src, .{
+ .ty = Type.initTag(.usize),
+ .val = Value.initTag(.one),
+ });
+ const index_2 = try addZIRUnOp(mod, &loop_scope.base, cond_src, .deref, index_ptr);
+ const index_plus_one = try addZIRBinOp(mod, &loop_scope.base, for_src, .add, index_2, one);
+ _ = try addZIRBinOp(mod, &loop_scope.base, for_src, .store, index_ptr, index_plus_one);
+
+ // looping stuff
+ const loop = try addZIRInstLoop(mod, &for_scope.base, for_src, .{
+ .instructions = try for_scope.arena.dupe(*zir.Inst, loop_scope.instructions.items),
+ });
+ const for_block = try addZIRInstBlock(mod, scope, for_src, .block, .{
+ .instructions = try for_scope.arena.dupe(*zir.Inst, for_scope.instructions.items),
+ });
+
+ // while body
+ const then_src = tree.token_locs[for_node.body.lastToken()].start;
+ var then_scope: Scope.GenZIR = .{
+ .parent = &cond_scope.base,
+ .decl = cond_scope.decl,
+ .arena = cond_scope.arena,
+ .instructions = .{},
+ };
+ defer then_scope.instructions.deinit(mod.gpa);
+
+ // Most result location types can be forwarded directly; however
+ // if we need to write to a pointer which has an inferred type,
+ // proper type inference requires peer type resolution on the while's
+ // branches.
+ const branch_rl: ResultLoc = switch (rl) {
+ .discard, .none, .ty, .ptr, .ref => rl,
+ .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = for_block },
+ };
+
+ var index_scope: Scope.LocalPtr = undefined;
+ const then_sub_scope = blk: {
+ const payload = for_node.payload.castTag(.PointerIndexPayload).?;
+ const is_ptr = payload.ptr_token != null;
+ const value_name = tree.tokenSlice(payload.value_symbol.firstToken());
+ if (!mem.eql(u8, value_name, "_")) {
+ return mod.failNode(&then_scope.base, payload.value_symbol, "TODO implement for value payload", .{});
+ } else if (is_ptr) {
+ return mod.failTok(&then_scope.base, payload.ptr_token.?, "pointer modifier invalid on discard", .{});
+ }
+
+ const index_symbol_node = payload.index_symbol orelse
+ break :blk &then_scope.base;
+
+ const index_name = tree.tokenSlice(index_symbol_node.firstToken());
+ if (mem.eql(u8, index_name, "_")) {
+ break :blk &then_scope.base;
+ }
+ // TODO make this const without an extra copy?
+ index_scope = .{
+ .parent = &then_scope.base,
+ .gen_zir = &then_scope,
+ .name = index_name,
+ .ptr = index_ptr,
+ };
+ break :blk &index_scope.base;
+ };
+
+ const then_result = try expr(mod, then_sub_scope, branch_rl, for_node.body);
+ if (!then_result.tag.isNoReturn()) {
+ _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{
+ .block = cond_block,
+ .operand = then_result,
+ }, .{});
+ }
+ condbr.positionals.then_body = .{
+ .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
+ };
+
+ // else branch
+ var else_scope: Scope.GenZIR = .{
+ .parent = &cond_scope.base,
+ .decl = cond_scope.decl,
+ .arena = cond_scope.arena,
+ .instructions = .{},
+ };
+ defer else_scope.instructions.deinit(mod.gpa);
+
+ if (for_node.@"else") |else_node| {
+ const else_src = tree.token_locs[else_node.body.lastToken()].start;
+ const else_result = try expr(mod, &else_scope.base, branch_rl, else_node.body);
+ if (!else_result.tag.isNoReturn()) {
+ _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.Break, .{
+ .block = for_block,
+ .operand = else_result,
+ }, .{});
+ }
+ } else {
+ const else_src = tree.token_locs[for_node.lastToken()].start;
+ _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.BreakVoid, .{
+ .block = for_block,
+ }, .{});
+ }
+ condbr.positionals.else_body = .{
+ .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
+ };
+ return &for_block.base;
+}
+
+fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[cfe.ltoken].start;
+ if (cfe.getRHS()) |rhs_node| {
+ if (nodeMayNeedMemoryLocation(rhs_node)) {
+ const ret_ptr = try addZIRNoOp(mod, scope, src, .ret_ptr);
+ const operand = try expr(mod, scope, .{ .ptr = ret_ptr }, rhs_node);
+ return addZIRUnOp(mod, scope, src, .@"return", operand);
+ } else {
+ const fn_ret_ty = try addZIRNoOp(mod, scope, src, .ret_type);
+ const operand = try expr(mod, scope, .{ .ty = fn_ret_ty }, rhs_node);
+ return addZIRUnOp(mod, scope, src, .@"return", operand);
+ }
+ } else {
+ return addZIRNoOp(mod, scope, src, .returnvoid);
+ }
+}
+
+fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const tree = scope.tree();
+ const ident_name = try identifierTokenString(mod, scope, ident.token);
+ const src = tree.token_locs[ident.token].start;
+ if (mem.eql(u8, ident_name, "_")) {
+ return mod.failNode(scope, &ident.base, "TODO implement '_' identifier", .{});
+ }
+
+ if (getSimplePrimitiveValue(ident_name)) |typed_value| {
+ const result = try addZIRInstConst(mod, scope, src, typed_value);
+ return rlWrap(mod, scope, rl, result);
+ }
+
+ if (ident_name.len >= 2) integer: {
+ const first_c = ident_name[0];
+ if (first_c == 'i' or first_c == 'u') {
+ const is_signed = first_c == 'i';
+ const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) {
+ error.Overflow => return mod.failNode(
+ scope,
+ &ident.base,
+ "primitive integer type '{}' exceeds maximum bit width of 65535",
+ .{ident_name},
+ ),
+ error.InvalidCharacter => break :integer,
+ };
+ const val = switch (bit_count) {
+ 8 => if (is_signed) Value.initTag(.i8_type) else Value.initTag(.u8_type),
+ 16 => if (is_signed) Value.initTag(.i16_type) else Value.initTag(.u16_type),
+ 32 => if (is_signed) Value.initTag(.i32_type) else Value.initTag(.u32_type),
+ 64 => if (is_signed) Value.initTag(.i64_type) else Value.initTag(.u64_type),
+ else => {
+ const int_type_payload = try scope.arena().create(Value.Payload.IntType);
+ int_type_payload.* = .{ .signed = is_signed, .bits = bit_count };
+ const result = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initPayload(&int_type_payload.base),
+ });
+ return rlWrap(mod, scope, rl, result);
+ },
+ };
+ const result = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = val,
+ });
+ return rlWrap(mod, scope, rl, result);
+ }
+ }
+
+ // Local variables, including function parameters.
+ {
+ var s = scope;
+ while (true) switch (s.tag) {
+ .local_val => {
+ const local_val = s.cast(Scope.LocalVal).?;
+ if (mem.eql(u8, local_val.name, ident_name)) {
+ return rlWrap(mod, scope, rl, local_val.inst);
+ }
+ s = local_val.parent;
+ },
+ .local_ptr => {
+ const local_ptr = s.cast(Scope.LocalPtr).?;
+ if (mem.eql(u8, local_ptr.name, ident_name)) {
+ return rlWrapPtr(mod, scope, rl, local_ptr.ptr);
+ }
+ s = local_ptr.parent;
+ },
+ .gen_zir => s = s.cast(Scope.GenZIR).?.parent,
+ else => break,
+ };
+ }
+
+ if (mod.lookupDeclName(scope, ident_name)) |decl| {
+ return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{}));
+ }
+
+ return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name});
+}
+
+fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const unparsed_bytes = tree.tokenSlice(str_lit.token);
+ const arena = scope.arena();
+
+ var bad_index: usize = undefined;
+ const bytes = std.zig.parseStringLiteral(arena, unparsed_bytes, &bad_index) catch |err| switch (err) {
+ error.InvalidCharacter => {
+ const bad_byte = unparsed_bytes[bad_index];
+ const src = tree.token_locs[str_lit.token].start;
+ return mod.fail(scope, src + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
+ },
+ else => |e| return e,
+ };
+
+ const src = tree.token_locs[str_lit.token].start;
+ return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
+}
+
+fn multilineStrLiteral(mod: *Module, scope: *Scope, node: *ast.Node.MultilineStringLiteral) !*zir.Inst {
+ const tree = scope.tree();
+ const lines = node.linesConst();
+ const src = tree.token_locs[lines[0]].start;
+
+ // line lengths and new lines
+ var len = lines.len - 1;
+ for (lines) |line| {
+ // 2 for the '//' + 1 for '\n'
+ len += tree.tokenSlice(line).len - 3;
+ }
+
+ const bytes = try scope.arena().alloc(u8, len);
+ var i: usize = 0;
+ for (lines) |line, line_i| {
+ if (line_i != 0) {
+ bytes[i] = '\n';
+ i += 1;
+ }
+ const slice = tree.tokenSlice(line);
+ mem.copy(u8, bytes[i..], slice[2 .. slice.len - 1]);
+ i += slice.len - 3;
+ }
+
+ return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
+}
+
+fn charLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) !*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.token].start;
+ const slice = tree.tokenSlice(node.token);
+
+ var bad_index: usize = undefined;
+ const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) {
+ error.InvalidCharacter => {
+ const bad_byte = slice[bad_index];
+ return mod.fail(scope, src + bad_index, "invalid character: '{c}'\n", .{bad_byte});
+ },
+ };
+
+ const int_payload = try scope.arena().create(Value.Payload.Int_u64);
+ int_payload.* = .{ .int = value };
+ return addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.comptime_int),
+ .val = Value.initPayload(&int_payload.base),
+ });
+}
+
+fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const arena = scope.arena();
+ const tree = scope.tree();
+ const prefixed_bytes = tree.tokenSlice(int_lit.token);
+ const base = if (mem.startsWith(u8, prefixed_bytes, "0x"))
+ 16
+ else if (mem.startsWith(u8, prefixed_bytes, "0o"))
+ 8
+ else if (mem.startsWith(u8, prefixed_bytes, "0b"))
+ 2
+ else
+ @as(u8, 10);
+
+ const bytes = if (base == 10)
+ prefixed_bytes
+ else
+ prefixed_bytes[2..];
+
+ if (std.fmt.parseInt(u64, bytes, base)) |small_int| {
+ const int_payload = try arena.create(Value.Payload.Int_u64);
+ int_payload.* = .{ .int = small_int };
+ const src = tree.token_locs[int_lit.token].start;
+ return addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.comptime_int),
+ .val = Value.initPayload(&int_payload.base),
+ });
+ } else |err| {
+ return mod.failTok(scope, int_lit.token, "TODO implement int literals that don't fit in a u64", .{});
+ }
+}
+
+fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const arena = scope.arena();
+ const tree = scope.tree();
+ const bytes = tree.tokenSlice(float_lit.token);
+ if (bytes.len > 2 and bytes[1] == 'x') {
+ return mod.failTok(scope, float_lit.token, "TODO hex floats", .{});
+ }
+
+ const val = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) {
+ error.InvalidCharacter => unreachable, // validated by tokenizer
+ };
+ const float_payload = try arena.create(Value.Payload.Float_128);
+ float_payload.* = .{ .val = val };
+ const src = tree.token_locs[float_lit.token].start;
+ return addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.comptime_float),
+ .val = Value.initPayload(&float_payload.base),
+ });
+}
+
+fn undefLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const arena = scope.arena();
+ const tree = scope.tree();
+ const src = tree.token_locs[node.token].start;
+ return addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.@"undefined"),
+ .val = Value.initTag(.undef),
+ });
+}
+
+fn boolLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const arena = scope.arena();
+ const tree = scope.tree();
+ const src = tree.token_locs[node.token].start;
+ return addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.bool),
+ .val = switch (tree.token_ids[node.token]) {
+ .Keyword_true => Value.initTag(.bool_true),
+ .Keyword_false => Value.initTag(.bool_false),
+ else => unreachable,
+ },
+ });
+}
+
+fn nullLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const arena = scope.arena();
+ const tree = scope.tree();
+ const src = tree.token_locs[node.token].start;
+ return addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.@"null"),
+ .val = Value.initTag(.null_value),
+ });
+}
+
+fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst {
+ if (asm_node.outputs.len != 0) {
+ return mod.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{});
+ }
+ const arena = scope.arena();
+ const tree = scope.tree();
+
+ const inputs = try arena.alloc(*zir.Inst, asm_node.inputs.len);
+ const args = try arena.alloc(*zir.Inst, asm_node.inputs.len);
+
+ const src = tree.token_locs[asm_node.asm_token].start;
+
+ const str_type = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.const_slice_u8_type),
+ });
+ const str_type_rl: ResultLoc = .{ .ty = str_type };
+
+ for (asm_node.inputs) |input, i| {
+ // TODO semantically analyze constraints
+ inputs[i] = try expr(mod, scope, str_type_rl, input.constraint);
+ args[i] = try expr(mod, scope, .none, input.expr);
+ }
+
+ const return_type = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.void_type),
+ });
+ const asm_inst = try addZIRInst(mod, scope, src, zir.Inst.Asm, .{
+ .asm_source = try expr(mod, scope, str_type_rl, asm_node.template),
+ .return_type = return_type,
+ }, .{
+ .@"volatile" = asm_node.volatile_token != null,
+ //.clobbers = TODO handle clobbers
+ .inputs = inputs,
+ .args = args,
+ });
+ return asm_inst;
+}
+
+fn ensureBuiltinParamCount(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall, count: u32) !void {
+ if (call.params_len == count)
+ return;
+
+ const s = if (count == 1) "" else "s";
+ return mod.failTok(scope, call.builtin_token, "expected {} parameter{}, found {}", .{ count, s, call.params_len });
+}
+
+fn simpleCast(
+ mod: *Module,
+ scope: *Scope,
+ rl: ResultLoc,
+ call: *ast.Node.BuiltinCall,
+ inst_tag: zir.Inst.Tag,
+) InnerError!*zir.Inst {
+ try ensureBuiltinParamCount(mod, scope, call, 2);
+ const tree = scope.tree();
+ const src = tree.token_locs[call.builtin_token].start;
+ const params = call.params();
+ const dest_type = try typeExpr(mod, scope, params[0]);
+ const rhs = try expr(mod, scope, .none, params[1]);
+ const result = try addZIRBinOp(mod, scope, src, inst_tag, dest_type, rhs);
+ return rlWrap(mod, scope, rl, result);
+}
+
+fn ptrToInt(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
+ try ensureBuiltinParamCount(mod, scope, call, 1);
+ const operand = try expr(mod, scope, .none, call.params()[0]);
+ const tree = scope.tree();
+ const src = tree.token_locs[call.builtin_token].start;
+ return addZIRUnOp(mod, scope, src, .ptrtoint, operand);
+}
+
+fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
+ try ensureBuiltinParamCount(mod, scope, call, 2);
+ const tree = scope.tree();
+ const src = tree.token_locs[call.builtin_token].start;
+ const params = call.params();
+ const dest_type = try typeExpr(mod, scope, params[0]);
+ switch (rl) {
+ .none => return try expr(mod, scope, .{ .ty = dest_type }, params[1]),
+ .discard => {
+ const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
+ _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
+ return result;
+ },
+ .ref => {
+ const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
+ return addZIRUnOp(mod, scope, result.src, .ref, result);
+ },
+ .ty => |result_ty| {
+ const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
+ return addZIRBinOp(mod, scope, src, .as, result_ty, result);
+ },
+ .ptr => |result_ptr| {
+ const casted_result_ptr = try addZIRBinOp(mod, scope, src, .coerce_result_ptr, dest_type, result_ptr);
+ return expr(mod, scope, .{ .ptr = casted_result_ptr }, params[1]);
+ },
+ .bitcasted_ptr => |bitcasted_ptr| {
+ // TODO here we should be able to resolve the inference; we now have a type for the result.
+ return mod.failTok(scope, call.builtin_token, "TODO implement @as with result location @bitCast", .{});
+ },
+ .inferred_ptr => |result_alloc| {
+ // TODO here we should be able to resolve the inference; we now have a type for the result.
+ return mod.failTok(scope, call.builtin_token, "TODO implement @as with inferred-type result location pointer", .{});
+ },
+ .block_ptr => |block_ptr| {
+ const casted_block_ptr = try addZIRInst(mod, scope, src, zir.Inst.CoerceResultBlockPtr, .{
+ .dest_type = dest_type,
+ .block = block_ptr,
+ }, .{});
+ return expr(mod, scope, .{ .ptr = casted_block_ptr }, params[1]);
+ },
+ }
+}
+
+fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
+ try ensureBuiltinParamCount(mod, scope, call, 2);
+ const tree = scope.tree();
+ const src = tree.token_locs[call.builtin_token].start;
+ const params = call.params();
+ const dest_type = try typeExpr(mod, scope, params[0]);
+ switch (rl) {
+ .none => {
+ const operand = try expr(mod, scope, .none, params[1]);
+ return addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
+ },
+ .discard => {
+ const operand = try expr(mod, scope, .none, params[1]);
+ const result = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
+ _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
+ return result;
+ },
+ .ref => {
+ const operand = try expr(mod, scope, .ref, params[1]);
+ const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand);
+ return result;
+ },
+ .ty => |result_ty| {
+ const result = try expr(mod, scope, .none, params[1]);
+ const bitcasted = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, result);
+ return addZIRBinOp(mod, scope, src, .as, result_ty, bitcasted);
+ },
+ .ptr => |result_ptr| {
+ const casted_result_ptr = try addZIRUnOp(mod, scope, src, .bitcast_result_ptr, result_ptr);
+ return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, params[1]);
+ },
+ .bitcasted_ptr => |bitcasted_ptr| {
+ return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with result location another @bitCast", .{});
+ },
+ .block_ptr => |block_ptr| {
+ return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with result location inferred peer types", .{});
+ },
+ .inferred_ptr => |result_alloc| {
+ // TODO here we should be able to resolve the inference; we now have a type for the result.
+ return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with inferred-type result location pointer", .{});
+ },
+ }
+}
+
+fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const builtin_name = tree.tokenSlice(call.builtin_token);
+
+ // We handle the different builtins manually because they have different semantics depending
+ // on the function. For example, `@as` and others participate in result location semantics,
+ // and `@cImport` creates a special scope that collects a .c source code text buffer.
+ // Also, some builtins have a variable number of parameters.
+
+ if (mem.eql(u8, builtin_name, "@ptrToInt")) {
+ return rlWrap(mod, scope, rl, try ptrToInt(mod, scope, call));
+ } else if (mem.eql(u8, builtin_name, "@as")) {
+ return as(mod, scope, rl, call);
+ } else if (mem.eql(u8, builtin_name, "@floatCast")) {
+ return simpleCast(mod, scope, rl, call, .floatcast);
+ } else if (mem.eql(u8, builtin_name, "@intCast")) {
+ return simpleCast(mod, scope, rl, call, .intcast);
+ } else if (mem.eql(u8, builtin_name, "@bitCast")) {
+ return bitCast(mod, scope, rl, call);
+ } else if (mem.eql(u8, builtin_name, "@breakpoint")) {
+ const src = tree.token_locs[call.builtin_token].start;
+ return rlWrap(mod, scope, rl, try addZIRNoOp(mod, scope, src, .breakpoint));
+ } else {
+ return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{}'", .{builtin_name});
+ }
+}
+
+fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const lhs = try expr(mod, scope, .none, node.lhs);
+
+ const param_nodes = node.params();
+ const args = try scope.getGenZIR().arena.alloc(*zir.Inst, param_nodes.len);
+ for (param_nodes) |param_node, i| {
+ const param_src = tree.token_locs[param_node.firstToken()].start;
+ const param_type = try addZIRInst(mod, scope, param_src, zir.Inst.ParamType, .{
+ .func = lhs,
+ .arg_index = i,
+ }, .{});
+ args[i] = try expr(mod, scope, .{ .ty = param_type }, param_node);
+ }
+
+ const src = tree.token_locs[node.lhs.firstToken()].start;
+ const result = try addZIRInst(mod, scope, src, zir.Inst.Call, .{
+ .func = lhs,
+ .args = args,
+ }, .{});
+ // TODO function call with result location
+ return rlWrap(mod, scope, rl, result);
+}
+
+fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[unreach_node.token].start;
+ return addZIRNoOp(mod, scope, src, .@"unreachable");
+}
+
+fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {
+ const simple_types = std.ComptimeStringMap(Value.Tag, .{
+ .{ "u8", .u8_type },
+ .{ "i8", .i8_type },
+ .{ "isize", .isize_type },
+ .{ "usize", .usize_type },
+ .{ "c_short", .c_short_type },
+ .{ "c_ushort", .c_ushort_type },
+ .{ "c_int", .c_int_type },
+ .{ "c_uint", .c_uint_type },
+ .{ "c_long", .c_long_type },
+ .{ "c_ulong", .c_ulong_type },
+ .{ "c_longlong", .c_longlong_type },
+ .{ "c_ulonglong", .c_ulonglong_type },
+ .{ "c_longdouble", .c_longdouble_type },
+ .{ "f16", .f16_type },
+ .{ "f32", .f32_type },
+ .{ "f64", .f64_type },
+ .{ "f128", .f128_type },
+ .{ "c_void", .c_void_type },
+ .{ "bool", .bool_type },
+ .{ "void", .void_type },
+ .{ "type", .type_type },
+ .{ "anyerror", .anyerror_type },
+ .{ "comptime_int", .comptime_int_type },
+ .{ "comptime_float", .comptime_float_type },
+ .{ "noreturn", .noreturn_type },
+ });
+ if (simple_types.get(name)) |tag| {
+ return TypedValue{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(tag),
+ };
+ }
+ return null;
+}
+
+fn nodeMayNeedMemoryLocation(start_node: *ast.Node) bool {
+ var node = start_node;
+ while (true) {
+ switch (node.tag) {
+ .Root,
+ .Use,
+ .TestDecl,
+ .DocComment,
+ .SwitchCase,
+ .SwitchElse,
+ .Else,
+ .Payload,
+ .PointerPayload,
+ .PointerIndexPayload,
+ .ContainerField,
+ .ErrorTag,
+ .FieldInitializer,
+ => unreachable,
+
+ .Return,
+ .Break,
+ .Continue,
+ .BitNot,
+ .BoolNot,
+ .VarDecl,
+ .Defer,
+ .AddressOf,
+ .OptionalType,
+ .Negation,
+ .NegationWrap,
+ .Resume,
+ .ArrayType,
+ .ArrayTypeSentinel,
+ .PtrType,
+ .SliceType,
+ .Suspend,
+ .AnyType,
+ .ErrorType,
+ .FnProto,
+ .AnyFrameType,
+ .IntegerLiteral,
+ .FloatLiteral,
+ .EnumLiteral,
+ .StringLiteral,
+ .MultilineStringLiteral,
+ .CharLiteral,
+ .BoolLiteral,
+ .NullLiteral,
+ .UndefinedLiteral,
+ .Unreachable,
+ .Identifier,
+ .ErrorSetDecl,
+ .ContainerDecl,
+ .Asm,
+ .Add,
+ .AddWrap,
+ .ArrayCat,
+ .ArrayMult,
+ .Assign,
+ .AssignBitAnd,
+ .AssignBitOr,
+ .AssignBitShiftLeft,
+ .AssignBitShiftRight,
+ .AssignBitXor,
+ .AssignDiv,
+ .AssignSub,
+ .AssignSubWrap,
+ .AssignMod,
+ .AssignAdd,
+ .AssignAddWrap,
+ .AssignMul,
+ .AssignMulWrap,
+ .BangEqual,
+ .BitAnd,
+ .BitOr,
+ .BitShiftLeft,
+ .BitShiftRight,
+ .BitXor,
+ .BoolAnd,
+ .BoolOr,
+ .Div,
+ .EqualEqual,
+ .ErrorUnion,
+ .GreaterOrEqual,
+ .GreaterThan,
+ .LessOrEqual,
+ .LessThan,
+ .MergeErrorSets,
+ .Mod,
+ .Mul,
+ .MulWrap,
+ .Range,
+ .Period,
+ .Sub,
+ .SubWrap,
+ .Slice,
+ .Deref,
+ .ArrayAccess,
+ .Block,
+ => return false,
+
+ // Forward the question to a sub-expression.
+ .GroupedExpression => node = node.castTag(.GroupedExpression).?.expr,
+ .Try => node = node.castTag(.Try).?.rhs,
+ .Await => node = node.castTag(.Await).?.rhs,
+ .Catch => node = node.castTag(.Catch).?.rhs,
+ .OrElse => node = node.castTag(.OrElse).?.rhs,
+ .Comptime => node = node.castTag(.Comptime).?.expr,
+ .Nosuspend => node = node.castTag(.Nosuspend).?.expr,
+ .UnwrapOptional => node = node.castTag(.UnwrapOptional).?.lhs,
+
+ // True because these are exactly the expressions we need memory locations for.
+ .ArrayInitializer,
+ .ArrayInitializerDot,
+ .StructInitializer,
+ .StructInitializerDot,
+ => return true,
+
+ // True because depending on comptime conditions, sub-expressions
+ // may be the kind that need memory locations.
+ .While,
+ .For,
+ .Switch,
+ .Call,
+ .BuiltinCall, // TODO some of these can return false
+ .LabeledBlock,
+ => return true,
+
+ // Depending on AST properties, they may need memory locations.
+ .If => return node.castTag(.If).?.@"else" != null,
+ }
+ }
+}
+
+/// Applies `rl` semantics to `inst`. Expressions which do not do their own handling of
+/// result locations must call this function on their result.
+/// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer.
+/// If the `ResultLoc` is `ty`, it will coerce the result to the type.
+fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerError!*zir.Inst {
+ switch (rl) {
+ .none => return result,
+ .discard => {
+ // Emit a compile error for discarding error values.
+ _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
+ return result;
+ },
+ .ref => {
+ // We need a pointer but we have a value.
+ return addZIRUnOp(mod, scope, result.src, .ref, result);
+ },
+ .ty => |ty_inst| return addZIRBinOp(mod, scope, result.src, .as, ty_inst, result),
+ .ptr => |ptr_inst| {
+ const casted_result = try addZIRInst(mod, scope, result.src, zir.Inst.CoerceToPtrElem, .{
+ .ptr = ptr_inst,
+ .value = result,
+ }, .{});
+ _ = try addZIRBinOp(mod, scope, result.src, .store, ptr_inst, casted_result);
+ return casted_result;
+ },
+ .bitcasted_ptr => |bitcasted_ptr| {
+ return mod.fail(scope, result.src, "TODO implement rlWrap .bitcasted_ptr", .{});
+ },
+ .inferred_ptr => |alloc| {
+ return mod.fail(scope, result.src, "TODO implement rlWrap .inferred_ptr", .{});
+ },
+ .block_ptr => |block_ptr| {
+ return mod.fail(scope, result.src, "TODO implement rlWrap .block_ptr", .{});
+ },
+ }
+}
+
+fn rlWrapVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, result: void) InnerError!*zir.Inst {
+ const src = scope.tree().token_locs[node.firstToken()].start;
+ const void_inst = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.void),
+ .val = Value.initTag(.void_value),
+ });
+ return rlWrap(mod, scope, rl, void_inst);
+}
+
+fn rlWrapPtr(mod: *Module, scope: *Scope, rl: ResultLoc, ptr: *zir.Inst) InnerError!*zir.Inst {
+ if (rl == .ref) return ptr;
+
+ return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, ptr.src, .deref, ptr));
+}
+
+pub fn addZIRInstSpecial(
+ mod: *Module,
+ scope: *Scope,
+ src: usize,
+ comptime T: type,
+ positionals: std.meta.fieldInfo(T, "positionals").field_type,
+ kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
+) !*T {
+ const gen_zir = scope.getGenZIR();
+ try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
+ const inst = try gen_zir.arena.create(T);
+ inst.* = .{
+ .base = .{
+ .tag = T.base_tag,
+ .src = src,
+ },
+ .positionals = positionals,
+ .kw_args = kw_args,
+ };
+ gen_zir.instructions.appendAssumeCapacity(&inst.base);
+ return inst;
+}
+
+pub fn addZIRNoOpT(mod: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst.NoOp {
+ const gen_zir = scope.getGenZIR();
+ try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
+ const inst = try gen_zir.arena.create(zir.Inst.NoOp);
+ inst.* = .{
+ .base = .{
+ .tag = tag,
+ .src = src,
+ },
+ .positionals = .{},
+ .kw_args = .{},
+ };
+ gen_zir.instructions.appendAssumeCapacity(&inst.base);
+ return inst;
+}
+
+pub fn addZIRNoOp(mod: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst {
+ const inst = try addZIRNoOpT(mod, scope, src, tag);
+ return &inst.base;
+}
+
+pub fn addZIRUnOp(
+ mod: *Module,
+ scope: *Scope,
+ src: usize,
+ tag: zir.Inst.Tag,
+ operand: *zir.Inst,
+) !*zir.Inst {
+ const gen_zir = scope.getGenZIR();
+ try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
+ const inst = try gen_zir.arena.create(zir.Inst.UnOp);
+ inst.* = .{
+ .base = .{
+ .tag = tag,
+ .src = src,
+ },
+ .positionals = .{
+ .operand = operand,
+ },
+ .kw_args = .{},
+ };
+ gen_zir.instructions.appendAssumeCapacity(&inst.base);
+ return &inst.base;
+}
+
+pub fn addZIRBinOp(
+ mod: *Module,
+ scope: *Scope,
+ src: usize,
+ tag: zir.Inst.Tag,
+ lhs: *zir.Inst,
+ rhs: *zir.Inst,
+) !*zir.Inst {
+ const gen_zir = scope.getGenZIR();
+ try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
+ const inst = try gen_zir.arena.create(zir.Inst.BinOp);
+ inst.* = .{
+ .base = .{
+ .tag = tag,
+ .src = src,
+ },
+ .positionals = .{
+ .lhs = lhs,
+ .rhs = rhs,
+ },
+ .kw_args = .{},
+ };
+ gen_zir.instructions.appendAssumeCapacity(&inst.base);
+ return &inst.base;
+}
+
+pub fn addZIRInstBlock(
+ mod: *Module,
+ scope: *Scope,
+ src: usize,
+ tag: zir.Inst.Tag,
+ body: zir.Module.Body,
+) !*zir.Inst.Block {
+ const gen_zir = scope.getGenZIR();
+ try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
+ const inst = try gen_zir.arena.create(zir.Inst.Block);
+ inst.* = .{
+ .base = .{
+ .tag = tag,
+ .src = src,
+ },
+ .positionals = .{
+ .body = body,
+ },
+ .kw_args = .{},
+ };
+ gen_zir.instructions.appendAssumeCapacity(&inst.base);
+ return inst;
+}
+
+pub fn addZIRInst(
+ mod: *Module,
+ scope: *Scope,
+ src: usize,
+ comptime T: type,
+ positionals: std.meta.fieldInfo(T, "positionals").field_type,
+ kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
+) !*zir.Inst {
+ const inst_special = try addZIRInstSpecial(mod, scope, src, T, positionals, kw_args);
+ return &inst_special.base;
+}
+
+/// TODO The existence of this function is a workaround for a bug in stage1.
+pub fn addZIRInstConst(mod: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*zir.Inst {
+ const P = std.meta.fieldInfo(zir.Inst.Const, "positionals").field_type;
+ return addZIRInst(mod, scope, src, zir.Inst.Const, P{ .typed_value = typed_value }, .{});
+}
+
+/// TODO The existence of this function is a workaround for a bug in stage1.
+pub fn addZIRInstLoop(mod: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Loop {
+ const P = std.meta.fieldInfo(zir.Inst.Loop, "positionals").field_type;
+ return addZIRInstSpecial(mod, scope, src, zir.Inst.Loop, P{ .body = body }, .{});
+}
diff --git a/src/bigfloat.cpp b/src/bigfloat.cpp
deleted file mode 100644
index a2a3a3b69cbbfbc91b80d85db97d1d7b256058c3..0000000000000000000000000000000000000000
--- a/src/bigfloat.cpp
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (c) 2017 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#include "bigfloat.hpp"
-#include "bigint.hpp"
-#include "buffer.hpp"
-#include "softfloat.hpp"
-#include "parse_f128.h"
-#include
-#include
-#include
-
-
-void bigfloat_init_128(BigFloat *dest, float128_t x) {
- dest->value = x;
-}
-
-void bigfloat_init_16(BigFloat *dest, float16_t x) {
- f16_to_f128M(x, &dest->value);
-}
-
-void bigfloat_init_32(BigFloat *dest, float x) {
- float32_t f32_val;
- memcpy(&f32_val, &x, sizeof(float));
- f32_to_f128M(f32_val, &dest->value);
-}
-
-void bigfloat_init_64(BigFloat *dest, double x) {
- float64_t f64_val;
- memcpy(&f64_val, &x, sizeof(double));
- f64_to_f128M(f64_val, &dest->value);
-}
-
-void bigfloat_init_bigfloat(BigFloat *dest, const BigFloat *x) {
- memcpy(&dest->value, &x->value, sizeof(float128_t));
-}
-
-void bigfloat_init_bigint(BigFloat *dest, const BigInt *op) {
- ui32_to_f128M(0, &dest->value);
- if (op->digit_count == 0)
- return;
-
- float128_t base;
- ui64_to_f128M(UINT64_MAX, &base);
- const uint64_t *digits = bigint_ptr(op);
-
- for (size_t i = op->digit_count - 1;;) {
- float128_t digit_f128;
- ui64_to_f128M(digits[i], &digit_f128);
-
- f128M_mulAdd(&dest->value, &base, &digit_f128, &dest->value);
-
- if (i == 0) {
- if (op->is_negative) {
- float128_t zero_f128;
- ui32_to_f128M(0, &zero_f128);
- f128M_sub(&zero_f128, &dest->value, &dest->value);
- }
- return;
- }
- i -= 1;
- }
-}
-
-Error bigfloat_init_buf(BigFloat *dest, const uint8_t *buf_ptr, size_t buf_len) {
- char *str_begin = (char *)buf_ptr;
- char *str_end;
-
- errno = 0;
- dest->value = parse_f128(str_begin, &str_end);
- if (errno) {
- return ErrorOverflow;
- }
-
- assert(str_end <= ((char*)buf_ptr) + buf_len);
- return ErrorNone;
-}
-
-void bigfloat_add(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
- f128M_add(&op1->value, &op2->value, &dest->value);
-}
-
-void bigfloat_negate(BigFloat *dest, const BigFloat *op) {
- float128_t zero_f128;
- ui32_to_f128M(0, &zero_f128);
- f128M_sub(&zero_f128, &op->value, &dest->value);
-}
-
-void bigfloat_sub(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
- f128M_sub(&op1->value, &op2->value, &dest->value);
-}
-
-void bigfloat_mul(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
- f128M_mul(&op1->value, &op2->value, &dest->value);
-}
-
-void bigfloat_div(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
- f128M_div(&op1->value, &op2->value, &dest->value);
-}
-
-void bigfloat_div_trunc(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
- f128M_div(&op1->value, &op2->value, &dest->value);
- f128M_roundToInt(&dest->value, softfloat_round_minMag, false, &dest->value);
-}
-
-void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
- f128M_div(&op1->value, &op2->value, &dest->value);
- f128M_roundToInt(&dest->value, softfloat_round_min, false, &dest->value);
-}
-
-void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
- f128M_rem(&op1->value, &op2->value, &dest->value);
-}
-
-void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
- f128M_rem(&op1->value, &op2->value, &dest->value);
- f128M_add(&dest->value, &op2->value, &dest->value);
- f128M_rem(&dest->value, &op2->value, &dest->value);
-}
-
-void bigfloat_append_buf(Buf *buf, const BigFloat *op) {
- const size_t extra_len = 100;
- size_t old_len = buf_len(buf);
- buf_resize(buf, old_len + extra_len);
-
- // TODO actually print f128
- float64_t f64_value = f128M_to_f64(&op->value);
- double double_value;
- memcpy(&double_value, &f64_value, sizeof(double));
-
- int len = snprintf(buf_ptr(buf) + old_len, extra_len, "%f", double_value);
- assert(len > 0);
- buf_resize(buf, old_len + len);
-}
-
-Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2) {
- if (f128M_lt(&op1->value, &op2->value)) {
- return CmpLT;
- } else if (f128M_eq(&op1->value, &op2->value)) {
- return CmpEQ;
- } else {
- return CmpGT;
- }
-}
-
-float16_t bigfloat_to_f16(const BigFloat *bigfloat) {
- return f128M_to_f16(&bigfloat->value);
-}
-
-float bigfloat_to_f32(const BigFloat *bigfloat) {
- float32_t f32_value = f128M_to_f32(&bigfloat->value);
- float result;
- memcpy(&result, &f32_value, sizeof(float));
- return result;
-}
-
-double bigfloat_to_f64(const BigFloat *bigfloat) {
- float64_t f64_value = f128M_to_f64(&bigfloat->value);
- double result;
- memcpy(&result, &f64_value, sizeof(double));
- return result;
-}
-
-float128_t bigfloat_to_f128(const BigFloat *bigfloat) {
- return bigfloat->value;
-}
-
-Cmp bigfloat_cmp_zero(const BigFloat *bigfloat) {
- float128_t zero_float;
- ui32_to_f128M(0, &zero_float);
- if (f128M_lt(&bigfloat->value, &zero_float)) {
- return CmpLT;
- } else if (f128M_eq(&bigfloat->value, &zero_float)) {
- return CmpEQ;
- } else {
- return CmpGT;
- }
-}
-
-bool bigfloat_has_fraction(const BigFloat *bigfloat) {
- float128_t floored;
- f128M_roundToInt(&bigfloat->value, softfloat_round_minMag, false, &floored);
- return !f128M_eq(&floored, &bigfloat->value);
-}
-
-void bigfloat_sqrt(BigFloat *dest, const BigFloat *op) {
- f128M_sqrt(&op->value, &dest->value);
-}
-
-bool bigfloat_is_nan(const BigFloat *op) {
- return f128M_isSignalingNaN(&op->value);
-}
diff --git a/src/bigfloat.hpp b/src/bigfloat.hpp
deleted file mode 100644
index 3ed6624fdcbff7d57629792599320558db71542a..0000000000000000000000000000000000000000
--- a/src/bigfloat.hpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2017 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#ifndef ZIG_BIGFLOAT_HPP
-#define ZIG_BIGFLOAT_HPP
-
-#include "bigint.hpp"
-#include "error.hpp"
-#include
-#include
-
-#include "softfloat_types.h"
-
-
-struct BigFloat {
- float128_t value;
-};
-
-struct Buf;
-
-void bigfloat_init_16(BigFloat *dest, float16_t x);
-void bigfloat_init_32(BigFloat *dest, float x);
-void bigfloat_init_64(BigFloat *dest, double x);
-void bigfloat_init_128(BigFloat *dest, float128_t x);
-void bigfloat_init_bigfloat(BigFloat *dest, const BigFloat *x);
-void bigfloat_init_bigint(BigFloat *dest, const BigInt *op);
-Error bigfloat_init_buf(BigFloat *dest, const uint8_t *buf_ptr, size_t buf_len);
-
-float16_t bigfloat_to_f16(const BigFloat *bigfloat);
-float bigfloat_to_f32(const BigFloat *bigfloat);
-double bigfloat_to_f64(const BigFloat *bigfloat);
-float128_t bigfloat_to_f128(const BigFloat *bigfloat);
-
-void bigfloat_add(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
-void bigfloat_negate(BigFloat *dest, const BigFloat *op);
-void bigfloat_sub(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
-void bigfloat_mul(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
-void bigfloat_div(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
-void bigfloat_div_trunc(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
-void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
-void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
-void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
-void bigfloat_sqrt(BigFloat *dest, const BigFloat *op);
-void bigfloat_append_buf(Buf *buf, const BigFloat *op);
-Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2);
-
-bool bigfloat_is_nan(const BigFloat *op);
-
-// convenience functions
-Cmp bigfloat_cmp_zero(const BigFloat *bigfloat);
-bool bigfloat_has_fraction(const BigFloat *bigfloat);
-
-#endif
diff --git a/src/bigint.cpp b/src/bigint.cpp
deleted file mode 100644
index 79a05e95a52a862c8728be21318642cf2fecda45..0000000000000000000000000000000000000000
--- a/src/bigint.cpp
+++ /dev/null
@@ -1,1786 +0,0 @@
-/*
- * Copyright (c) 2017 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#include "bigfloat.hpp"
-#include "bigint.hpp"
-#include "buffer.hpp"
-#include "list.hpp"
-#include "os.hpp"
-#include "softfloat.hpp"
-
-#include
-#include
-
-static uint64_t bigint_as_unsigned(const BigInt *bigint);
-
-static void bigint_normalize(BigInt *dest) {
- const uint64_t *digits = bigint_ptr(dest);
-
- size_t last_nonzero_digit = SIZE_MAX;
- for (size_t i = 0; i < dest->digit_count; i += 1) {
- uint64_t digit = digits[i];
- if (digit != 0) {
- last_nonzero_digit = i;
- }
- }
- if (last_nonzero_digit == SIZE_MAX) {
- dest->is_negative = false;
- dest->digit_count = 0;
- } else {
- dest->digit_count = last_nonzero_digit + 1;
- if (last_nonzero_digit == 0) {
- dest->data.digit = digits[0];
- }
- }
-}
-
-static uint8_t digit_to_char(uint8_t digit, bool uppercase) {
- if (digit <= 9) {
- return digit + '0';
- } else if (digit <= 35) {
- return (digit - 10) + (uppercase ? 'A' : 'a');
- } else {
- zig_unreachable();
- }
-}
-
-size_t bigint_bits_needed(const BigInt *op) {
- size_t full_bits = op->digit_count * 64;
- size_t leading_zero_count = bigint_clz(op, full_bits);
- size_t bits_needed = full_bits - leading_zero_count;
- return bits_needed + op->is_negative;
-}
-
-static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count) {
- if (bit_count == 0 || op->digit_count == 0) {
- bigint_init_unsigned(dest, 0);
- return;
- }
- if (op->is_negative) {
- BigInt negated = {0};
- bigint_negate(&negated, op);
-
- BigInt inverted = {0};
- bigint_not(&inverted, &negated, bit_count, false);
-
- BigInt one = {0};
- bigint_init_unsigned(&one, 1);
-
- bigint_add(dest, &inverted, &one);
- return;
- }
-
- dest->is_negative = false;
- const uint64_t *op_digits = bigint_ptr(op);
- if (op->digit_count == 1) {
- dest->data.digit = op_digits[0];
- if (bit_count < 64) {
- dest->data.digit &= (1ULL << bit_count) - 1;
- }
- dest->digit_count = 1;
- bigint_normalize(dest);
- return;
- }
- size_t digits_to_copy = bit_count / 64;
- size_t leftover_bits = bit_count % 64;
- dest->digit_count = digits_to_copy + ((leftover_bits == 0) ? 0 : 1);
- if (dest->digit_count == 1 && leftover_bits == 0) {
- dest->data.digit = op_digits[0];
- if (dest->data.digit == 0) dest->digit_count = 0;
- return;
- }
- dest->data.digits = heap::c_allocator.allocate_nonzero(dest->digit_count);
- for (size_t i = 0; i < digits_to_copy; i += 1) {
- uint64_t digit = (i < op->digit_count) ? op_digits[i] : 0;
- dest->data.digits[i] = digit;
- }
- if (leftover_bits != 0) {
- uint64_t digit = (digits_to_copy < op->digit_count) ? op_digits[digits_to_copy] : 0;
- dest->data.digits[digits_to_copy] = digit & ((1ULL << leftover_bits) - 1);
- }
- bigint_normalize(dest);
-}
-
-static bool bit_at_index(const BigInt *bi, size_t index) {
- size_t digit_index = index / 64;
- if (digit_index >= bi->digit_count)
- return false;
- size_t digit_bit_index = index % 64;
- const uint64_t *digits = bigint_ptr(bi);
- uint64_t digit = digits[digit_index];
- return ((digit >> digit_bit_index) & 0x1) == 0x1;
-}
-
-static void from_twos_complement(BigInt *dest, const BigInt *src, size_t bit_count, bool is_signed) {
- assert(!src->is_negative);
-
- if (bit_count == 0 || src->digit_count == 0) {
- bigint_init_unsigned(dest, 0);
- return;
- }
-
- if (is_signed && bit_at_index(src, bit_count - 1)) {
- BigInt negative_one = {0};
- bigint_init_signed(&negative_one, -1);
-
- BigInt minus_one = {0};
- bigint_add(&minus_one, src, &negative_one);
-
- BigInt inverted = {0};
- bigint_not(&inverted, &minus_one, bit_count, false);
-
- bigint_negate(dest, &inverted);
- return;
-
- }
-
- bigint_init_bigint(dest, src);
-}
-
-void bigint_init_unsigned(BigInt *dest, uint64_t x) {
- if (x == 0) {
- dest->digit_count = 0;
- dest->is_negative = false;
- return;
- }
- dest->digit_count = 1;
- dest->data.digit = x;
- dest->is_negative = false;
-}
-
-void bigint_init_signed(BigInt *dest, int64_t x) {
- if (x >= 0) {
- return bigint_init_unsigned(dest, x);
- }
- dest->is_negative = true;
- dest->digit_count = 1;
- dest->data.digit = ((uint64_t)(-(x + 1))) + 1;
-}
-
-void bigint_init_data(BigInt *dest, const uint64_t *digits, size_t digit_count, bool is_negative) {
- if (digit_count == 0) {
- return bigint_init_unsigned(dest, 0);
- } else if (digit_count == 1) {
- dest->digit_count = 1;
- dest->data.digit = digits[0];
- dest->is_negative = is_negative;
- bigint_normalize(dest);
- return;
- }
-
- dest->digit_count = digit_count;
- dest->is_negative = is_negative;
- dest->data.digits = heap::c_allocator.allocate_nonzero(digit_count);
- memcpy(dest->data.digits, digits, sizeof(uint64_t) * digit_count);
-
- bigint_normalize(dest);
-}
-
-void bigint_init_bigint(BigInt *dest, const BigInt *src) {
- if (src->digit_count == 0) {
- return bigint_init_unsigned(dest, 0);
- } else if (src->digit_count == 1) {
- dest->digit_count = 1;
- dest->data.digit = src->data.digit;
- dest->is_negative = src->is_negative;
- return;
- }
- dest->is_negative = src->is_negative;
- dest->digit_count = src->digit_count;
- dest->data.digits = heap::c_allocator.allocate_nonzero(dest->digit_count);
- memcpy(dest->data.digits, src->data.digits, sizeof(uint64_t) * dest->digit_count);
-}
-
-void bigint_deinit(BigInt *bi) {
- if (bi->digit_count > 1)
- heap::c_allocator.deallocate(bi->data.digits, bi->digit_count);
-}
-
-void bigint_init_bigfloat(BigInt *dest, const BigFloat *op) {
- float128_t zero;
- ui32_to_f128M(0, &zero);
-
- dest->is_negative = f128M_lt(&op->value, &zero);
- float128_t abs_val;
- if (dest->is_negative) {
- f128M_sub(&zero, &op->value, &abs_val);
- } else {
- memcpy(&abs_val, &op->value, sizeof(float128_t));
- }
-
- float128_t max_u64;
- ui64_to_f128M(UINT64_MAX, &max_u64);
- if (f128M_le(&abs_val, &max_u64)) {
- dest->digit_count = 1;
- dest->data.digit = f128M_to_ui64(&op->value, softfloat_round_minMag, false);
- bigint_normalize(dest);
- return;
- }
-
- float128_t amt;
- f128M_div(&abs_val, &max_u64, &amt);
- float128_t remainder;
- f128M_rem(&abs_val, &max_u64, &remainder);
-
- dest->digit_count = 2;
- dest->data.digits = heap::c_allocator.allocate_nonzero(dest->digit_count);
- dest->data.digits[0] = f128M_to_ui64(&remainder, softfloat_round_minMag, false);
- dest->data.digits[1] = f128M_to_ui64(&amt, softfloat_round_minMag, false);
- bigint_normalize(dest);
-}
-
-bool bigint_fits_in_bits(const BigInt *bn, size_t bit_count, bool is_signed) {
- assert(bn->digit_count != 1 || bn->data.digit != 0);
- if (bit_count == 0) {
- return bigint_cmp_zero(bn) == CmpEQ;
- }
- if (bn->digit_count == 0) {
- return true;
- }
-
- if (!is_signed) {
- if(bn->is_negative) return false;
- size_t full_bits = bn->digit_count * 64;
- size_t leading_zero_count = bigint_clz(bn, full_bits);
- return bit_count >= full_bits - leading_zero_count;
- }
-
- BigInt one = {0};
- bigint_init_unsigned(&one, 1);
-
- BigInt shl_amt = {0};
- bigint_init_unsigned(&shl_amt, bit_count - 1);
-
- BigInt max_value_plus_one = {0};
- bigint_shl(&max_value_plus_one, &one, &shl_amt);
-
- BigInt max_value = {0};
- bigint_sub(&max_value, &max_value_plus_one, &one);
-
- BigInt min_value = {0};
- bigint_negate(&min_value, &max_value_plus_one);
-
- Cmp min_cmp = bigint_cmp(bn, &min_value);
- Cmp max_cmp = bigint_cmp(bn, &max_value);
-
- return (min_cmp == CmpGT || min_cmp == CmpEQ) && (max_cmp == CmpLT || max_cmp == CmpEQ);
-}
-
-void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian) {
- if (bit_count == 0)
- return;
-
- BigInt twos_comp = {0};
- to_twos_complement(&twos_comp, big_int, bit_count);
-
- const uint64_t *twos_comp_digits = bigint_ptr(&twos_comp);
-
- size_t bits_in_last_digit = bit_count % 64;
- if (bits_in_last_digit == 0) bits_in_last_digit = 64;
- size_t bytes_in_last_digit = (bits_in_last_digit + 7) / 8;
- size_t unwritten_byte_count = 8 - bytes_in_last_digit;
-
- if (is_big_endian) {
- size_t last_digit_index = (bit_count - 1) / 64;
- size_t digit_index = last_digit_index;
- size_t buf_index = 0;
- for (;;) {
- uint64_t x = (digit_index < twos_comp.digit_count) ? twos_comp_digits[digit_index] : 0;
-
- for (size_t byte_index = 7;;) {
- uint8_t byte = x & 0xff;
- if (digit_index == last_digit_index) {
- buf[buf_index + byte_index - unwritten_byte_count] = byte;
- if (byte_index == unwritten_byte_count) break;
- } else {
- buf[buf_index + byte_index] = byte;
- }
-
- if (byte_index == 0) break;
- byte_index -= 1;
- x >>= 8;
- }
-
- if (digit_index == 0) break;
- digit_index -= 1;
- if (digit_index == last_digit_index) {
- buf_index += bytes_in_last_digit;
- } else {
- buf_index += 8;
- }
- }
- } else {
- size_t digit_count = (bit_count + 63) / 64;
- size_t buf_index = 0;
- for (size_t digit_index = 0; digit_index < digit_count; digit_index += 1) {
- uint64_t x = (digit_index < twos_comp.digit_count) ? twos_comp_digits[digit_index] : 0;
-
- for (size_t byte_index = 0;
- byte_index < 8 && (digit_index + 1 < digit_count || byte_index < bytes_in_last_digit);
- byte_index += 1)
- {
- uint8_t byte = x & 0xff;
- buf[buf_index] = byte;
- buf_index += 1;
- x >>= 8;
- }
- }
- }
-}
-
-
-void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian,
- bool is_signed)
-{
- if (bit_count == 0) {
- bigint_init_unsigned(dest, 0);
- return;
- }
-
- dest->digit_count = (bit_count + 63) / 64;
- uint64_t *digits;
- if (dest->digit_count == 1) {
- digits = &dest->data.digit;
- } else {
- digits = heap::c_allocator.allocate_nonzero(dest->digit_count);
- dest->data.digits = digits;
- }
-
- size_t bits_in_last_digit = bit_count % 64;
- if (bits_in_last_digit == 0) {
- bits_in_last_digit = 64;
- }
- size_t bytes_in_last_digit = (bits_in_last_digit + 7) / 8;
- size_t unread_byte_count = 8 - bytes_in_last_digit;
-
- if (is_big_endian) {
- size_t buf_index = 0;
- uint64_t digit = 0;
- for (size_t byte_index = unread_byte_count; byte_index < 8; byte_index += 1) {
- uint8_t byte = buf[buf_index];
- buf_index += 1;
- digit <<= 8;
- digit |= byte;
- }
- digits[dest->digit_count - 1] = digit;
- for (size_t digit_index = 1; digit_index < dest->digit_count; digit_index += 1) {
- digit = 0;
- for (size_t byte_index = 0; byte_index < 8; byte_index += 1) {
- uint8_t byte = buf[buf_index];
- buf_index += 1;
- digit <<= 8;
- digit |= byte;
- }
- digits[dest->digit_count - 1 - digit_index] = digit;
- }
- } else {
- size_t buf_index = 0;
- for (size_t digit_index = 0; digit_index < dest->digit_count; digit_index += 1) {
- uint64_t digit = 0;
- size_t end_byte_index = (digit_index == dest->digit_count - 1) ? bytes_in_last_digit : 8;
- for (size_t byte_index = 0; byte_index < end_byte_index; byte_index += 1) {
- uint64_t byte = buf[buf_index];
- buf_index += 1;
-
- digit |= byte << (8 * byte_index);
- }
- digits[digit_index] = digit;
- }
- }
-
- if (is_signed) {
- bigint_normalize(dest);
- BigInt tmp = {0};
- bigint_init_bigint(&tmp, dest);
- from_twos_complement(dest, &tmp, bit_count, true);
- } else {
- dest->is_negative = false;
- bigint_normalize(dest);
- }
-}
-
-#if defined(_MSC_VER)
-static bool add_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) {
- *result = op1 + op2;
- return *result < op1 || *result < op2;
-}
-
-static bool sub_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) {
- *result = op1 - op2;
- return *result > op1;
-}
-
-bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) {
- *result = op1 * op2;
-
- if (op1 == 0 || op2 == 0)
- return false;
-
- if (op1 > UINT64_MAX / op2)
- return true;
-
- if (op2 > UINT64_MAX / op1)
- return true;
-
- return false;
-}
-#else
-static bool add_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) {
- return __builtin_uaddll_overflow((unsigned long long)op1, (unsigned long long)op2,
- (unsigned long long *)result);
-}
-
-static bool sub_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) {
- return __builtin_usubll_overflow((unsigned long long)op1, (unsigned long long)op2,
- (unsigned long long *)result);
-}
-
-bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) {
- return __builtin_umulll_overflow((unsigned long long)op1, (unsigned long long)op2,
- (unsigned long long *)result);
-}
-#endif
-
-void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) {
- if (op1->digit_count == 0) {
- return bigint_init_bigint(dest, op2);
- }
- if (op2->digit_count == 0) {
- return bigint_init_bigint(dest, op1);
- }
- if (op1->is_negative == op2->is_negative) {
- dest->is_negative = op1->is_negative;
-
- const uint64_t *op1_digits = bigint_ptr(op1);
- const uint64_t *op2_digits = bigint_ptr(op2);
- bool overflow = add_u64_overflow(op1_digits[0], op2_digits[0], &dest->data.digit);
- if (overflow == 0 && op1->digit_count == 1 && op2->digit_count == 1) {
- dest->digit_count = 1;
- bigint_normalize(dest);
- return;
- }
- size_t i = 1;
- uint64_t first_digit = dest->data.digit;
- dest->data.digits = heap::c_allocator.allocate_nonzero(max(op1->digit_count, op2->digit_count) + 1);
- dest->data.digits[0] = first_digit;
-
- for (;;) {
- bool found_digit = false;
- uint64_t x = overflow;
- overflow = 0;
-
- if (i < op1->digit_count) {
- found_digit = true;
- uint64_t digit = op1_digits[i];
- overflow += add_u64_overflow(x, digit, &x);
- }
-
- if (i < op2->digit_count) {
- found_digit = true;
- uint64_t digit = op2_digits[i];
- overflow += add_u64_overflow(x, digit, &x);
- }
-
- dest->data.digits[i] = x;
- i += 1;
-
- if (!found_digit) {
- dest->digit_count = i;
- bigint_normalize(dest);
- return;
- }
- }
- }
- const BigInt *op_pos;
- const BigInt *op_neg;
- if (op1->is_negative) {
- op_neg = op1;
- op_pos = op2;
- } else {
- op_pos = op1;
- op_neg = op2;
- }
-
- BigInt op_neg_abs = {0};
- bigint_negate(&op_neg_abs, op_neg);
- const BigInt *bigger_op;
- const BigInt *smaller_op;
- switch (bigint_cmp(op_pos, &op_neg_abs)) {
- case CmpEQ:
- bigint_init_unsigned(dest, 0);
- return;
- case CmpLT:
- bigger_op = &op_neg_abs;
- smaller_op = op_pos;
- dest->is_negative = true;
- break;
- case CmpGT:
- bigger_op = op_pos;
- smaller_op = &op_neg_abs;
- dest->is_negative = false;
- break;
- }
- const uint64_t *bigger_op_digits = bigint_ptr(bigger_op);
- const uint64_t *smaller_op_digits = bigint_ptr(smaller_op);
- uint64_t overflow = sub_u64_overflow(bigger_op_digits[0], smaller_op_digits[0], &dest->data.digit);
- if (overflow == 0 && bigger_op->digit_count == 1 && smaller_op->digit_count == 1) {
- dest->digit_count = 1;
- bigint_normalize(dest);
- return;
- }
- uint64_t first_digit = dest->data.digit;
- dest->data.digits = heap::c_allocator.allocate_nonzero(bigger_op->digit_count);
- dest->data.digits[0] = first_digit;
- size_t i = 1;
-
- for (;;) {
- bool found_digit = false;
- uint64_t x = bigger_op_digits[i];
- uint64_t prev_overflow = overflow;
- overflow = 0;
-
- if (i < smaller_op->digit_count) {
- found_digit = true;
- uint64_t digit = smaller_op_digits[i];
- overflow += sub_u64_overflow(x, digit, &x);
- }
- if (sub_u64_overflow(x, prev_overflow, &x)) {
- found_digit = true;
- overflow += 1;
- }
- dest->data.digits[i] = x;
- i += 1;
-
- if (!found_digit || i >= bigger_op->digit_count)
- break;
- }
- assert(overflow == 0);
- dest->digit_count = i;
- bigint_normalize(dest);
-}
-
-void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) {
- BigInt unwrapped = {0};
- bigint_add(&unwrapped, op1, op2);
- bigint_truncate(dest, &unwrapped, bit_count, is_signed);
-}
-
-void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2) {
- BigInt op2_negated = {0};
- bigint_negate(&op2_negated, op2);
- return bigint_add(dest, op1, &op2_negated);
-}
-
-void bigint_sub_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) {
- BigInt op2_negated = {0};
- bigint_negate(&op2_negated, op2);
- return bigint_add_wrap(dest, op1, &op2_negated, bit_count, is_signed);
-}
-
-static void mul_overflow(uint64_t op1, uint64_t op2, uint64_t *lo, uint64_t *hi) {
- uint64_t u1 = (op1 & 0xffffffff);
- uint64_t v1 = (op2 & 0xffffffff);
- uint64_t t = (u1 * v1);
- uint64_t w3 = (t & 0xffffffff);
- uint64_t k = (t >> 32);
-
- op1 >>= 32;
- t = (op1 * v1) + k;
- k = (t & 0xffffffff);
- uint64_t w1 = (t >> 32);
-
- op2 >>= 32;
- t = (u1 * op2) + k;
- k = (t >> 32);
-
- *hi = (op1 * op2) + w1 + k;
- *lo = (t << 32) + w3;
-}
-
-static void mul_scalar(BigInt *dest, const BigInt *op, uint64_t scalar) {
- bigint_init_unsigned(dest, 0);
-
- BigInt bi_64;
- bigint_init_unsigned(&bi_64, 64);
-
- const uint64_t *op_digits = bigint_ptr(op);
- size_t i = op->digit_count - 1;
-
- for (;;) {
- BigInt shifted;
- bigint_shl(&shifted, dest, &bi_64);
-
- uint64_t result_scalar;
- uint64_t carry_scalar;
- mul_overflow(scalar, op_digits[i], &result_scalar, &carry_scalar);
-
- BigInt result;
- bigint_init_unsigned(&result, result_scalar);
-
- BigInt carry;
- bigint_init_unsigned(&carry, carry_scalar);
-
- BigInt carry_shifted;
- bigint_shl(&carry_shifted, &carry, &bi_64);
-
- BigInt tmp;
- bigint_add(&tmp, &shifted, &carry_shifted);
-
- bigint_add(dest, &tmp, &result);
-
- if (i == 0) {
- break;
- }
- i -= 1;
- }
-}
-
-void bigint_mul(BigInt *dest, const BigInt *op1, const BigInt *op2) {
- if (op1->digit_count == 0 || op2->digit_count == 0) {
- return bigint_init_unsigned(dest, 0);
- }
- const uint64_t *op1_digits = bigint_ptr(op1);
- const uint64_t *op2_digits = bigint_ptr(op2);
-
- uint64_t carry;
- mul_overflow(op1_digits[0], op2_digits[0], &dest->data.digit, &carry);
- if (carry == 0 && op1->digit_count == 1 && op2->digit_count == 1) {
- dest->is_negative = (op1->is_negative != op2->is_negative);
- dest->digit_count = 1;
- bigint_normalize(dest);
- return;
- }
-
- bigint_init_unsigned(dest, 0);
-
- BigInt bi_64;
- bigint_init_unsigned(&bi_64, 64);
-
- size_t i = op2->digit_count - 1;
- for (;;) {
- BigInt shifted;
- bigint_shl(&shifted, dest, &bi_64);
-
- BigInt scalar_result;
- mul_scalar(&scalar_result, op1, op2_digits[i]);
-
- bigint_add(dest, &scalar_result, &shifted);
-
- if (i == 0) {
- break;
- }
- i -= 1;
- }
-
- dest->is_negative = (op1->is_negative != op2->is_negative);
- bigint_normalize(dest);
-}
-
-void bigint_mul_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) {
- BigInt unwrapped = {0};
- bigint_mul(&unwrapped, op1, op2);
- bigint_truncate(dest, &unwrapped, bit_count, is_signed);
-}
-
-enum ZeroBehavior {
- /// \brief The returned value is undefined.
- ZB_Undefined,
- /// \brief The returned value is numeric_limits::max()
- ZB_Max,
- /// \brief The returned value is numeric_limits::digits
- ZB_Width
-};
-
-template struct LeadingZerosCounter {
- static std::size_t count(T Val, ZeroBehavior) {
- if (!Val)
- return std::numeric_limits::digits;
-
- // Bisection method.
- std::size_t ZeroBits = 0;
- for (T Shift = std::numeric_limits::digits >> 1; Shift; Shift >>= 1) {
- T Tmp = Val >> Shift;
- if (Tmp)
- Val = Tmp;
- else
- ZeroBits |= Shift;
- }
- return ZeroBits;
- }
-};
-
-#if __GNUC__ >= 4 || defined(_MSC_VER)
-template struct LeadingZerosCounter {
- static std::size_t count(T Val, ZeroBehavior ZB) {
- if (ZB != ZB_Undefined && Val == 0)
- return 32;
-
-#if defined(_MSC_VER)
- unsigned long Index;
- _BitScanReverse(&Index, Val);
- return Index ^ 31;
-#else
- return __builtin_clz(Val);
-#endif
- }
-};
-
-#if !defined(_MSC_VER) || defined(_M_X64)
-template struct LeadingZerosCounter {
- static std::size_t count(T Val, ZeroBehavior ZB) {
- if (ZB != ZB_Undefined && Val == 0)
- return 64;
-
-#if defined(_MSC_VER)
- unsigned long Index;
- _BitScanReverse64(&Index, Val);
- return Index ^ 63;
-#else
- return __builtin_clzll(Val);
-#endif
- }
-};
-#endif
-#endif
-
-/// \brief Count number of 0's from the most significant bit to the least
-/// stopping at the first 1.
-///
-/// Only unsigned integral types are allowed.
-///
-/// \param ZB the behavior on an input of 0. Only ZB_Width and ZB_Undefined are
-/// valid arguments.
-template
-std::size_t countLeadingZeros(T Val, ZeroBehavior ZB = ZB_Width) {
- static_assert(std::numeric_limits::is_integer &&
- !std::numeric_limits::is_signed,
- "Only unsigned integral types are allowed.");
- return LeadingZerosCounter::count(Val, ZB);
-}
-
-/// Make a 64-bit integer from a high / low pair of 32-bit integers.
-constexpr inline uint64_t Make_64(uint32_t High, uint32_t Low) {
- return ((uint64_t)High << 32) | (uint64_t)Low;
-}
-
-/// Return the high 32 bits of a 64 bit value.
-constexpr inline uint32_t Hi_32(uint64_t Value) {
- return static_cast(Value >> 32);
-}
-
-/// Return the low 32 bits of a 64 bit value.
-constexpr inline uint32_t Lo_32(uint64_t Value) {
- return static_cast(Value);
-}
-
-/// Implementation of Knuth's Algorithm D (Division of nonnegative integers)
-/// from "Art of Computer Programming, Volume 2", section 4.3.1, p. 272. The
-/// variables here have the same names as in the algorithm. Comments explain
-/// the algorithm and any deviation from it.
-static void KnuthDiv(uint32_t *u, uint32_t *v, uint32_t *q, uint32_t* r,
- unsigned m, unsigned n)
-{
- assert(u && "Must provide dividend");
- assert(v && "Must provide divisor");
- assert(q && "Must provide quotient");
- assert(u != v && u != q && v != q && "Must use different memory");
- assert(n>1 && "n must be > 1");
-
- // b denotes the base of the number system. In our case b is 2^32.
- const uint64_t b = uint64_t(1) << 32;
-
- // D1. [Normalize.] Set d = b / (v[n-1] + 1) and multiply all the digits of
- // u and v by d. Note that we have taken Knuth's advice here to use a power
- // of 2 value for d such that d * v[n-1] >= b/2 (b is the base). A power of
- // 2 allows us to shift instead of multiply and it is easy to determine the
- // shift amount from the leading zeros. We are basically normalizing the u
- // and v so that its high bits are shifted to the top of v's range without
- // overflow. Note that this can require an extra word in u so that u must
- // be of length m+n+1.
- unsigned shift = countLeadingZeros(v[n-1]);
- uint32_t v_carry = 0;
- uint32_t u_carry = 0;
- if (shift) {
- for (unsigned i = 0; i < m+n; ++i) {
- uint32_t u_tmp = u[i] >> (32 - shift);
- u[i] = (u[i] << shift) | u_carry;
- u_carry = u_tmp;
- }
- for (unsigned i = 0; i < n; ++i) {
- uint32_t v_tmp = v[i] >> (32 - shift);
- v[i] = (v[i] << shift) | v_carry;
- v_carry = v_tmp;
- }
- }
- u[m+n] = u_carry;
-
- // D2. [Initialize j.] Set j to m. This is the loop counter over the places.
- int j = m;
- do {
- // D3. [Calculate q'.].
- // Set qp = (u[j+n]*b + u[j+n-1]) / v[n-1]. (qp=qprime=q')
- // Set rp = (u[j+n]*b + u[j+n-1]) % v[n-1]. (rp=rprime=r')
- // Now test if qp == b or qp*v[n-2] > b*rp + u[j+n-2]; if so, decrease
- // qp by 1, increase rp by v[n-1], and repeat this test if rp < b. The test
- // on v[n-2] determines at high speed most of the cases in which the trial
- // value qp is one too large, and it eliminates all cases where qp is two
- // too large.
- uint64_t dividend = Make_64(u[j+n], u[j+n-1]);
- uint64_t qp = dividend / v[n-1];
- uint64_t rp = dividend % v[n-1];
- if (qp == b || qp*v[n-2] > b*rp + u[j+n-2]) {
- qp--;
- rp += v[n-1];
- if (rp < b && (qp == b || qp*v[n-2] > b*rp + u[j+n-2]))
- qp--;
- }
-
- // D4. [Multiply and subtract.] Replace (u[j+n]u[j+n-1]...u[j]) with
- // (u[j+n]u[j+n-1]..u[j]) - qp * (v[n-1]...v[1]v[0]). This computation
- // consists of a simple multiplication by a one-place number, combined with
- // a subtraction.
- // The digits (u[j+n]...u[j]) should be kept positive; if the result of
- // this step is actually negative, (u[j+n]...u[j]) should be left as the
- // true value plus b**(n+1), namely as the b's complement of
- // the true value, and a "borrow" to the left should be remembered.
- int64_t borrow = 0;
- for (unsigned i = 0; i < n; ++i) {
- uint64_t p = uint64_t(qp) * uint64_t(v[i]);
- int64_t subres = int64_t(u[j+i]) - borrow - Lo_32(p);
- u[j+i] = Lo_32(subres);
- borrow = Hi_32(p) - Hi_32(subres);
- }
- bool isNeg = u[j+n] < borrow;
- u[j+n] -= Lo_32(borrow);
-
- // D5. [Test remainder.] Set q[j] = qp. If the result of step D4 was
- // negative, go to step D6; otherwise go on to step D7.
- q[j] = Lo_32(qp);
- if (isNeg) {
- // D6. [Add back]. The probability that this step is necessary is very
- // small, on the order of only 2/b. Make sure that test data accounts for
- // this possibility. Decrease q[j] by 1
- q[j]--;
- // and add (0v[n-1]...v[1]v[0]) to (u[j+n]u[j+n-1]...u[j+1]u[j]).
- // A carry will occur to the left of u[j+n], and it should be ignored
- // since it cancels with the borrow that occurred in D4.
- bool carry = false;
- for (unsigned i = 0; i < n; i++) {
- uint32_t limit = std::min(u[j+i],v[i]);
- u[j+i] += v[i] + carry;
- carry = u[j+i] < limit || (carry && u[j+i] == limit);
- }
- u[j+n] += carry;
- }
-
- // D7. [Loop on j.] Decrease j by one. Now if j >= 0, go back to D3.
- } while (--j >= 0);
-
- // D8. [Unnormalize]. Now q[...] is the desired quotient, and the desired
- // remainder may be obtained by dividing u[...] by d. If r is non-null we
- // compute the remainder (urem uses this).
- if (r) {
- // The value d is expressed by the "shift" value above since we avoided
- // multiplication by d by using a shift left. So, all we have to do is
- // shift right here.
- if (shift) {
- uint32_t carry = 0;
- for (int i = n-1; i >= 0; i--) {
- r[i] = (u[i] >> shift) | carry;
- carry = u[i] << (32 - shift);
- }
- } else {
- for (int i = n-1; i >= 0; i--) {
- r[i] = u[i];
- }
- }
- }
-}
-
-// Implementation ported from LLVM/lib/Support/APInt.cpp
-static void bigint_unsigned_division(const BigInt *op1, const BigInt *op2, BigInt *Quotient, BigInt *Remainder) {
- Cmp cmp = bigint_cmp(op1, op2);
- if (cmp == CmpLT) {
- if (Quotient != nullptr) {
- bigint_init_unsigned(Quotient, 0);
- }
- if (Remainder != nullptr) {
- bigint_init_bigint(Remainder, op1);
- }
- return;
- }
- if (cmp == CmpEQ) {
- if (Quotient != nullptr) {
- bigint_init_unsigned(Quotient, 1);
- }
- if (Remainder != nullptr) {
- bigint_init_unsigned(Remainder, 0);
- }
- return;
- }
-
- const uint64_t *LHS = bigint_ptr(op1);
- const uint64_t *RHS = bigint_ptr(op2);
- unsigned lhsWords = op1->digit_count;
- unsigned rhsWords = op2->digit_count;
-
- // First, compose the values into an array of 32-bit words instead of
- // 64-bit words. This is a necessity of both the "short division" algorithm
- // and the Knuth "classical algorithm" which requires there to be native
- // operations for +, -, and * on an m bit value with an m*2 bit result. We
- // can't use 64-bit operands here because we don't have native results of
- // 128-bits. Furthermore, casting the 64-bit values to 32-bit values won't
- // work on large-endian machines.
- unsigned n = rhsWords * 2;
- unsigned m = (lhsWords * 2) - n;
-
- // Allocate space for the temporary values we need either on the stack, if
- // it will fit, or on the heap if it won't.
- uint32_t SPACE[128];
- uint32_t *U = nullptr;
- uint32_t *V = nullptr;
- uint32_t *Q = nullptr;
- uint32_t *R = nullptr;
- if ((Remainder?4:3)*n+2*m+1 <= 128) {
- U = &SPACE[0];
- V = &SPACE[m+n+1];
- Q = &SPACE[(m+n+1) + n];
- if (Remainder)
- R = &SPACE[(m+n+1) + n + (m+n)];
- } else {
- U = new uint32_t[m + n + 1];
- V = new uint32_t[n];
- Q = new uint32_t[m+n];
- if (Remainder)
- R = new uint32_t[n];
- }
-
- // Initialize the dividend
- memset(U, 0, (m+n+1)*sizeof(uint32_t));
- for (unsigned i = 0; i < lhsWords; ++i) {
- uint64_t tmp = LHS[i];
- U[i * 2] = Lo_32(tmp);
- U[i * 2 + 1] = Hi_32(tmp);
- }
- U[m+n] = 0; // this extra word is for "spill" in the Knuth algorithm.
-
- // Initialize the divisor
- memset(V, 0, (n)*sizeof(uint32_t));
- for (unsigned i = 0; i < rhsWords; ++i) {
- uint64_t tmp = RHS[i];
- V[i * 2] = Lo_32(tmp);
- V[i * 2 + 1] = Hi_32(tmp);
- }
-
- // initialize the quotient and remainder
- memset(Q, 0, (m+n) * sizeof(uint32_t));
- if (Remainder)
- memset(R, 0, n * sizeof(uint32_t));
-
- // Now, adjust m and n for the Knuth division. n is the number of words in
- // the divisor. m is the number of words by which the dividend exceeds the
- // divisor (i.e. m+n is the length of the dividend). These sizes must not
- // contain any zero words or the Knuth algorithm fails.
- for (unsigned i = n; i > 0 && V[i-1] == 0; i--) {
- n--;
- m++;
- }
- for (unsigned i = m+n; i > 0 && U[i-1] == 0; i--)
- m--;
-
- // If we're left with only a single word for the divisor, Knuth doesn't work
- // so we implement the short division algorithm here. This is much simpler
- // and faster because we are certain that we can divide a 64-bit quantity
- // by a 32-bit quantity at hardware speed and short division is simply a
- // series of such operations. This is just like doing short division but we
- // are using base 2^32 instead of base 10.
- assert(n != 0 && "Divide by zero?");
- if (n == 1) {
- uint32_t divisor = V[0];
- uint32_t remainder = 0;
- for (int i = m; i >= 0; i--) {
- uint64_t partial_dividend = Make_64(remainder, U[i]);
- if (partial_dividend == 0) {
- Q[i] = 0;
- remainder = 0;
- } else if (partial_dividend < divisor) {
- Q[i] = 0;
- remainder = Lo_32(partial_dividend);
- } else if (partial_dividend == divisor) {
- Q[i] = 1;
- remainder = 0;
- } else {
- Q[i] = Lo_32(partial_dividend / divisor);
- remainder = Lo_32(partial_dividend - (Q[i] * divisor));
- }
- }
- if (R)
- R[0] = remainder;
- } else {
- // Now we're ready to invoke the Knuth classical divide algorithm. In this
- // case n > 1.
- KnuthDiv(U, V, Q, R, m, n);
- }
-
- // If the caller wants the quotient
- if (Quotient) {
- Quotient->is_negative = false;
- Quotient->digit_count = lhsWords;
- if (lhsWords == 1) {
- Quotient->data.digit = Make_64(Q[1], Q[0]);
- } else {
- Quotient->data.digits = heap::c_allocator.allocate(lhsWords);
- for (size_t i = 0; i < lhsWords; i += 1) {
- Quotient->data.digits[i] = Make_64(Q[i*2+1], Q[i*2]);
- }
- }
- }
-
- // If the caller wants the remainder
- if (Remainder) {
- Remainder->is_negative = false;
- Remainder->digit_count = rhsWords;
- if (rhsWords == 1) {
- Remainder->data.digit = Make_64(R[1], R[0]);
- } else {
- Remainder->data.digits = heap::c_allocator.allocate(rhsWords);
- for (size_t i = 0; i < rhsWords; i += 1) {
- Remainder->data.digits[i] = Make_64(R[i*2+1], R[i*2]);
- }
- }
- }
-}
-
-void bigint_div_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2) {
- assert(op2->digit_count != 0); // division by zero
- if (op1->digit_count == 0) {
- bigint_init_unsigned(dest, 0);
- return;
- }
- const uint64_t *op1_digits = bigint_ptr(op1);
- const uint64_t *op2_digits = bigint_ptr(op2);
- if (op1->digit_count == 1 && op2->digit_count == 1) {
- dest->data.digit = op1_digits[0] / op2_digits[0];
- dest->digit_count = 1;
- dest->is_negative = op1->is_negative != op2->is_negative;
- bigint_normalize(dest);
- return;
- }
- if (op2->digit_count == 1 && op2_digits[0] == 1) {
- // X / 1 == X
- bigint_init_bigint(dest, op1);
- dest->is_negative = op1->is_negative != op2->is_negative;
- bigint_normalize(dest);
- return;
- }
-
- const BigInt *op1_positive;
- BigInt op1_positive_data;
- if (op1->is_negative) {
- bigint_negate(&op1_positive_data, op1);
- op1_positive = &op1_positive_data;
- } else {
- op1_positive = op1;
- }
-
- const BigInt *op2_positive;
- BigInt op2_positive_data;
- if (op2->is_negative) {
- bigint_negate(&op2_positive_data, op2);
- op2_positive = &op2_positive_data;
- } else {
- op2_positive = op2;
- }
-
- bigint_unsigned_division(op1_positive, op2_positive, dest, nullptr);
- dest->is_negative = op1->is_negative != op2->is_negative;
- bigint_normalize(dest);
-}
-
-void bigint_div_floor(BigInt *dest, const BigInt *op1, const BigInt *op2) {
- if (op1->is_negative != op2->is_negative) {
- bigint_div_trunc(dest, op1, op2);
- BigInt mult_again = {0};
- bigint_mul(&mult_again, dest, op2);
- mult_again.is_negative = op1->is_negative;
- if (bigint_cmp(&mult_again, op1) != CmpEQ) {
- BigInt tmp = {0};
- bigint_init_bigint(&tmp, dest);
- BigInt neg_one = {0};
- bigint_init_signed(&neg_one, -1);
- bigint_add(dest, &tmp, &neg_one);
- }
- bigint_normalize(dest);
- } else {
- bigint_div_trunc(dest, op1, op2);
- }
-}
-
-void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2) {
- assert(op2->digit_count != 0); // division by zero
- if (op1->digit_count == 0) {
- bigint_init_unsigned(dest, 0);
- return;
- }
- const uint64_t *op1_digits = bigint_ptr(op1);
- const uint64_t *op2_digits = bigint_ptr(op2);
-
- if (op1->digit_count == 1 && op2->digit_count == 1) {
- dest->data.digit = op1_digits[0] % op2_digits[0];
- dest->digit_count = 1;
- dest->is_negative = op1->is_negative;
- bigint_normalize(dest);
- return;
- }
- if (op2->digit_count == 2 && op2_digits[0] == 0 && op2_digits[1] == 1) {
- // special case this divisor
- bigint_init_unsigned(dest, op1_digits[0]);
- dest->is_negative = op1->is_negative;
- bigint_normalize(dest);
- return;
- }
-
- if (op2->digit_count == 1 && op2_digits[0] == 1) {
- // X % 1 == 0
- bigint_init_unsigned(dest, 0);
- return;
- }
-
- const BigInt *op1_positive;
- BigInt op1_positive_data;
- if (op1->is_negative) {
- bigint_negate(&op1_positive_data, op1);
- op1_positive = &op1_positive_data;
- } else {
- op1_positive = op1;
- }
-
- const BigInt *op2_positive;
- BigInt op2_positive_data;
- if (op2->is_negative) {
- bigint_negate(&op2_positive_data, op2);
- op2_positive = &op2_positive_data;
- } else {
- op2_positive = op2;
- }
-
- bigint_unsigned_division(op1_positive, op2_positive, nullptr, dest);
- dest->is_negative = op1->is_negative;
- bigint_normalize(dest);
-}
-
-void bigint_mod(BigInt *dest, const BigInt *op1, const BigInt *op2) {
- if (op1->is_negative) {
- BigInt first_rem;
- bigint_rem(&first_rem, op1, op2);
- first_rem.is_negative = !op2->is_negative;
- BigInt op2_minus_rem;
- bigint_add(&op2_minus_rem, op2, &first_rem);
- bigint_rem(dest, &op2_minus_rem, op2);
- dest->is_negative = false;
- } else {
- bigint_rem(dest, op1, op2);
- dest->is_negative = false;
- }
-}
-
-void bigint_or(BigInt *dest, const BigInt *op1, const BigInt *op2) {
- if (op1->digit_count == 0) {
- return bigint_init_bigint(dest, op2);
- }
- if (op2->digit_count == 0) {
- return bigint_init_bigint(dest, op1);
- }
- if (op1->is_negative || op2->is_negative) {
- size_t big_bit_count = max(bigint_bits_needed(op1), bigint_bits_needed(op2));
-
- BigInt twos_comp_op1 = {0};
- to_twos_complement(&twos_comp_op1, op1, big_bit_count);
-
- BigInt twos_comp_op2 = {0};
- to_twos_complement(&twos_comp_op2, op2, big_bit_count);
-
- BigInt twos_comp_dest = {0};
- bigint_or(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2);
-
- from_twos_complement(dest, &twos_comp_dest, big_bit_count, true);
- } else {
- dest->is_negative = false;
- const uint64_t *op1_digits = bigint_ptr(op1);
- const uint64_t *op2_digits = bigint_ptr(op2);
- if (op1->digit_count == 1 && op2->digit_count == 1) {
- dest->digit_count = 1;
- dest->data.digit = op1_digits[0] | op2_digits[0];
- bigint_normalize(dest);
- return;
- }
- dest->digit_count = max(op1->digit_count, op2->digit_count);
- dest->data.digits = heap::c_allocator.allocate_nonzero(dest->digit_count);
- for (size_t i = 0; i < dest->digit_count; i += 1) {
- uint64_t digit = 0;
- if (i < op1->digit_count) {
- digit |= op1_digits[i];
- }
- if (i < op2->digit_count) {
- digit |= op2_digits[i];
- }
- dest->data.digits[i] = digit;
- }
- bigint_normalize(dest);
- }
-}
-
-void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2) {
- if (op1->digit_count == 0 || op2->digit_count == 0) {
- return bigint_init_unsigned(dest, 0);
- }
- if (op1->is_negative || op2->is_negative) {
- size_t big_bit_count = max(bigint_bits_needed(op1), bigint_bits_needed(op2));
-
- BigInt twos_comp_op1 = {0};
- to_twos_complement(&twos_comp_op1, op1, big_bit_count);
-
- BigInt twos_comp_op2 = {0};
- to_twos_complement(&twos_comp_op2, op2, big_bit_count);
-
- BigInt twos_comp_dest = {0};
- bigint_and(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2);
-
- from_twos_complement(dest, &twos_comp_dest, big_bit_count, true);
- } else {
- dest->is_negative = false;
- const uint64_t *op1_digits = bigint_ptr(op1);
- const uint64_t *op2_digits = bigint_ptr(op2);
- if (op1->digit_count == 1 && op2->digit_count == 1) {
- dest->digit_count = 1;
- dest->data.digit = op1_digits[0] & op2_digits[0];
- bigint_normalize(dest);
- return;
- }
-
- dest->digit_count = max(op1->digit_count, op2->digit_count);
- dest->data.digits = heap::c_allocator.allocate_nonzero